# Задание A4 (Antigravity): изоляция учётных данных и последние долги ## Дата поступления 2026-08-21 ## База Проверочный HEAD: **`d2d6d95`**, `origin/main` = `d2d6d95`. Перед началом `git fetch`, зафиксировать фактический `BASE_SHA`. ## Ветка `antigravity/credential-isolation`. Ветки `state-layer`, `contract-gaps`, `release-readiness` влиты в `main` и больше не нужны. --- ## Что принято по A3 Проверено исполнением: - **Релизный ассет выложен и проверяем.** `package_url` → HTTP 200, GitHub Release `Hermes Hub v0.1.1` существует, sha256 загруженного архива совпадает с манифестом посимвольно (`29cfe190…`). Гейт теперь честно печатает `MANIFEST_LIVE=True, PACKAGE_LIVE=True, PACKAGE_HASH_VERIFIED=True`. - **Контракт исправлен ровно как требовалось**: два раздела — «Closed Gaps & Audit Status» со ссылками на коммиты и «Active Limitations & Backend Constraints», куда вернулись Gap 4 и Gap 12 с явными требованиями к UI. Gap 5 закрыт с обоснованием. Закрытое и оставшееся видны одновременно. - **Диагностическая команда** и **запись причины переключения маршрута** в журнал сделаны. - Граница соблюдена, прогон: headless 170 passed, с UI-зависимостями **217 passed, 0 падений**, ruff чисто, гейт PASSED. Это лучшая сдача за все раунды. Осталось немного. --- ## P0-1. Учётные данные не должны наследоваться subprocess'ом **Самый старый неисправленный дефект проекта** — отмечен ещё в первом аудите и жив до сих пор: ``` antigravity_adapter.py:43 custom_env = dict(os.environ) agy_subprocess.py:404 def _safe_env() ← вызовов: 0 ``` В subprocess `agy` уходит **весь** environment, включая `OPENAI_API_KEY`, `CODEX_TOKEN_*`, `OPENCODE_GO_API_KEY`, `DEEPSEEK_API_KEY` и всё остальное. При этом функция фильтрации `_safe_env()` написана автором изначально и не вызывается ни разу — то есть намерение было, реализация не подключена. Требуется: - сформировать окружение дочернего процесса **явно**: минимальный набор системных переменных плюс список разрешённых, а не копия `os.environ`; - профильная изоляция (`USERPROFILE`/`HOME`/`HOMEPATH`) сохраняется как есть — она нужна; - ключи провайдеров, не относящихся к вызываемому, в дочерний процесс не попадают; - секреты не пишутся в логи и в диагностическую команду. Правило на будущее: `os.environ.copy()` и `dict(os.environ)` для внешнего процесса — запрещены без фильтрации. Стоит закрепить тестом, который падает при появлении такого вызова в `src/`. **Тест обязателен:** запуск с подставным окружением, где заданы ключи нескольких провайдеров, и проверка, что в `custom_env` дочернего процесса их нет. ## P1-2. Оставшиеся долги Проверено на `d2d6d95`, всё ещё открыто: | Долг | Состояние | |---|---| | Комментарии `router_profiles.yaml` | 5 строк → 2 при сохранении | | `Registry.CurrentUser` в установщике | 2 вызова, реестр переменными окружения не перенаправляется | | `fastapi` / `uvicorn` | в обязательных зависимостях, хотя `gui_server` живёт в `legacy/` и не поставляется | | Сериализация Antigravity | `_AGY_INVOCATION_LOCK` держит вызовы профилей со своим auth по одному | По каждому — либо закрыть, либо записать как осознанный долг с обоснованием. Гадать не нужно: решение ваше, но оно должно быть зафиксировано, а не забыто. ## P1-3. Объяснение выбора провайдера Роутер уже собирает `failover_trail` и с A3 пишет причину переключения в журнал. Довести до полноценного объяснения выбора: какие профили рассматривались, какие отсеяны и почему (здоровье, квота, авторизация, лимит параллелизма, несовпадение возможностей), какой выбран. Данные для этого уже есть в `route_request` — нужно их сохранить в `router_metadata` и в событие, а не терять. Codex отрисует это отдельным заданием; вам — только источник. --- ## Чего делать НЕ нужно По итогам разбора документа «обновление по новостям ИИ от 21.08.2026»: **не начинать** Lifecycle Supervisor v2, Skill Registry v2, MCP Lifecycle Manager, Park/Resume и process-tree ownership. Причины: `lifecycle_supervisor`, `skill_registry` и `capability_matrix` были удалены как мёртвый код по решению предыдущего задания; подсистемы MCP в проекте нет вообще; роутер не владеет диалогом, поэтому park/resume нечего сохранять. Это спецификация другого продукта, и часть её должна принадлежать самому Hermes Agent, а не плагину-роутеру. Из того документа берём ровно два пункта — изоляцию учётных данных (P0-1) и объяснение выбора провайдера (P1-3). Остальное — в бэклог следующей версии. --- ## Критерии приёмки 1. Ни один файл зоны Codex (`router/ui/**`, `hermes_hub_app.py`, `tests/test_ui_*.py`) не изменён. 2. В дочерний процесс `agy` не попадают ключи провайдеров; доказано тестом с подставным окружением. 3. В `src/` нет `os.environ.copy()` / `dict(os.environ)` для внешнего процесса без фильтрации; закреплено тестом. 4. По каждому долгу из P1-2 — закрыт либо зафиксирован с обоснованием в отчёте. 5. Объяснение выбора провайдера доступно потребителю (метаданные ответа и событие), с перечнем отсеянных кандидатов и причинами. 6. Прогон **в обоих окружениях** — без UI-зависимостей и с `customtkinter`/`pillow`/`psutil`; обе команды и оба результата в отчёте. 7. `ruff check .` чисто; release gate PASSED **на финальном коммите**. 8. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`. ## Тег `v0.1.1` по-прежнему **не создавать**. Тег ставится после ручного прогона продукта человеком — это отдельное решение владельца. ## Порядок сдачи Передать точный `FINAL_COMMIT_SHA` и не вести разработку поверх него до вердикта.