# Задание A2 (Antigravity): закрыть пробелы контракта и релизную инфраструктуру

## Дата поступления
2026-08-21

## База
Проверочный HEAD: **`c6876e9`**, `origin/main` = `c6876e9`. Перед началом `git fetch`, зафиксировать фактический `BASE_SHA`.

## Ветка
`antigravity/contract-gaps`. Ветка `antigravity/state-layer` влита в `main` и больше не нужна — можно удалить вместе с worktree `../hermes-hub-state-layer`.

---

## Что принято по Заданию A

Проверено исполнением, претензий нет:

- контракт `docs/UI_STATE_CONTRACT.md` опубликован первым коммитом, 7 разделов, честный раздел «Backend gaps»;
- граница работ соблюдена — в зоне UI ни одного изменения;
- **namespace-пакет устранён по корню**: добавлен `src/antigravity_provider/__init__.py`, `__path__` теперь содержит ровно один каталог, установленная копия из `%LOCALAPPDATA%` больше не подмешивается. Два теста, падавшие только в полном прогоне, проходят;
- мёртвые модули удалены (`capability_matrix`, `skill_registry`, `lifecycle_supervisor`, −532 строки) — это был допустимый вариант;
- честность источников квот сохранена: `baseline` 6, `provider_auth` 1, `unconfigured` 1, ни одного `*_api`;
- headless-прогон 165 passed, ruff чисто, release gate PASSED.

**Исправлено ревьюером, не откатывать:** `0d9005f` изменил `start_profile_oauth` на 3-кортеж `(session_id, auth_url, port)` и обновил тесты, но не вызывающий код. Мастер распаковывал два значения, `ValueError` глушился обработчиком, и URL авторизации не появлялся — подключение Antigravity-аккаунта было сломано с того коммита. Исправлено в `39b1f92` вместе с полем `wizard.oauth_port`. Файл в зоне Codex, вам его не трогать, но знать нужно.

---

## ГРАНИЦА РАБОТ

Прежняя. Ваша зона — всё, кроме:

```
src/antigravity_provider/router/ui/**
src/antigravity_provider/router/hermes_hub_app.py
tests/test_ui_*.py
```

Codex параллельно выполняет фазы 2–6 по интерфейсу и опирается на контракт. **Любое расширение контракта — сначала документ, потом код**, иначе он будет писать против несуществующих полей.

---

## Приоритет: пробелы, блокирующие интерфейс

Эти пункты вы сами зафиксировали в разделе 7 контракта. Пока они открыты, Codex обязан рисовать «Н/Д» — то есть продукт остаётся полупустым.

### P0-1. Точечные события никто не публикует (gap 9)

Проверено на `c6876e9`:

```
publish(EVENT_REFRESH_STARTED            1
publish(EVENT_REFRESH_FAILED             1
publish(EVENT_SYSTEM_READINESS_CHANGED   1

EVENT_ACCOUNT_UPDATED   публикаций: 0
EVENT_ACCOUNT_ADDED     публикаций: 0
EVENT_QUOTA_UPDATED     публикаций: 0
EVENT_ROUTING_UPDATED   публикаций: 0
EVENT_AGENT_UPDATED     публикаций: 0
```

Константы объявлены, публикаторов нет — событийная модель существует только на бумаге, и любое изменение по-прежнему означает полный снапшот. Требование Задания A («изменение квоты одного аккаунта порождает событие с идентификатором аккаунта») не выполнено.

Нужно: канонический публикатор для каждого объявленного события либо удаление константы, если событие не нужно. Тест: изменение квоты одного аккаунта публикует ровно одно событие с его `profile_id` и не порождает полного пересбора.

### P0-2. Происхождение тарифа не доходит до `ProfileViewModel` (gap 6)

Из-за этого Codex не может показать `PlanBadge` — компонент готов и не используется. Нужно довести до модели и тариф, и его происхождение, чтобы UI отличал «известно от провайдера» от «выведено эвристикой».

### P0-3. `AgentViewModel` и `PipelineNode` неполны (gaps 7, 8)

- `AgentViewModel` без активной сессии и квоты → экран «Команда» показывает пустые строки;
- `PipelineNode` без идентичности аккаунта, состояния квоты и причины переключения → экран «Маршрутизация» не может ответить на главный вопрос «почему переключились и что с резервом».

Добавить поля и описать в контракте. Причину переключения брать из реального состояния маршрута, а не выводить задним числом.

---

## P1. Достоверность данных

### P1-4. Реальный сбор квот (gaps 1, 2)

Сейчас **ни один провайдер** не отдаёт живых чисел: все корзины `baseline`. Структура мульти-корзин у Antigravity есть, значения и время сброса не измеряются.

Довести реальный сбор там, где провайдер это отдаёт. Правила прежние: локально вычисленное не помечать источником провайдера, отсутствие данных отдавать как отсутствие, а не как число. Для каждого провайдера в контракте указать, что именно теперь измеряется.

### P1-5. Свежесть и защита от устаревшего ответа (gaps 3, 10, 11)

Вы сами отметили: `seq` наружу не выставлен, вложенные квотные воркеры могут перестроить состояние до завершения, а `seq` записывается **до** окончания медленной работы — то есть доказательства, что поздний ответ не перезапишет свежий, нет.

Нужно: публичный `seq` в снапшоте, запись `seq` по завершении работы, тест с искусственной задержкой, где поздний ответ гарантированно отбрасывается.

### P1-6. Релизная инфраструктура

`package_url` из живого манифеста по-прежнему отдаёт **HTTP 404** — проверено сейчас. Порядок публикации: build → checksum → upload artifact → verify → publish manifest → verify feed. Манифест не должен рекламировать несуществующий артефакт. `dist/checksums.txt` — только из `scripts/build_dist.py`.

**Тег `v0.1.1` не создавать.**

---

## P2. Долги

- **Комментарии `router_profiles.yaml`** — стало лучше (было 5 → 0, теперь 5 → 2), но потеря остаётся. Либо round-trip YAML, либо явная фиксация долга с указанием, что файл перезаписывается приложением.
- **`HKCU` в тестах установщика** — `Registry.CurrentUser` по-прежнему используется дважды; переменными окружения реестр не перенаправляется. Вынести реальный запуск в явный integration-режим либо параметризовать ключ.
- **Сериализация Antigravity** — `_AGY_INVOCATION_LOCK` держит вызовы профилей со своим auth по одному. Либо отказаться от глобальной записи `gemini:antigravity` в пользу изоляции через `USERPROFILE`, либо записать как осознанный долг с обоснованием.
- **`fastapi`/`uvicorn`** остаются обязательными зависимостями, хотя `gui_server` живёт в `legacy/` и не поставляется. Привести в соответствие.

---

## Критерии приёмки

1. Ни один файл зоны Codex не изменён.
2. Для каждого объявленного события есть публикатор либо константа удалена; тест доказывает точечность.
3. `PlanBadge` можно показать: тариф и его происхождение доходят до `ProfileViewModel`.
4. `AgentViewModel` и `PipelineNode` содержат поля, которых требуют экраны «Команда» и «Маршрутизация».
5. Раздел «Backend gaps» контракта сокращён; по каждому закрытому пункту указан коммит. Что осталось — осталось честно.
6. Ни одно локально вычисленное значение не помечено источником провайдера.
7. Поздний ответ не перезаписывает свежий — доказано тестом с задержкой, а не порядком строк.
8. `curl -I <package_url>` → HTTP 200, sha256 совпадает с артефактом.
9. Прогон **в обоих окружениях**: без UI-зависимостей и с установленными `customtkinter`, `pillow`, `psutil`. Оба зелёных, обе команды и оба результата в отчёте.
10. `ruff check .` чисто; release gate PASSED **на финальном коммите**, без функциональных коммитов после него.
11. Отчёт содержит `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status` и точный `X passed / Y skipped / Z failed`.

## Порядок сдачи
Ревьюеру передать точный `FINAL_COMMIT_SHA` и не вести разработку поверх него до вердикта.
