hermes-hub/agents/inbox/2026-08-21-A2-antigravity-close-contract-gaps.md
Hermes Team e8a404be03 docs(task): next Antigravity assignment — close the gaps its own contract declares
Seven of the twelve backend gaps in UI_STATE_CONTRACT.md block the UI outright:
no targeted event has a publisher, plan provenance never reaches
ProfileViewModel, and AgentViewModel/PipelineNode lack the fields the Team and
Routing screens need. Verified on c6876e9, alongside the release asset still
returning 404 and YAML comment loss now at 5 to 2 rather than 5 to 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 08:16:44 +07:00

126 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Задание 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 параллельно выполняет фазы 26 по интерфейсу и опирается на контракт. **Любое расширение контракта — сначала документ, потом код**, иначе он будет писать против несуществующих полей.
---
## Приоритет: пробелы, блокирующие интерфейс
Эти пункты вы сами зафиксировали в разделе 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` и не вести разработку поверх него до вердикта.