hermes-hub/agents/inbox/2026-08-21-A-antigravity-state-layer.md
Hermes Team 93883f9ce4 docs(task): split Plan A and UI redesign into two non-overlapping assignments
The UI redesign draft duplicated eight Plan A phases on the same files
(accounts_view, routing_view, hermes_hub_app, unified_health), which would have
put two agents into the same merge conflicts. Ownership is now split by file
path, with docs/UI_STATE_CONTRACT.md as the interface between them.

Four draft items were already implemented at 50fde5f and are marked as such
rather than reassigned: OAuth URL copy-before-open, keyed account card reuse,
the no-fake-metrics rule, and the importorskip guard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:13:59 +07:00

131 lines
12 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.

# Задание A (Antigravity): слой состояния и данных
## Дата поступления
2026-08-21
## База
Проверочный HEAD на момент выдачи: **`50fde5f`**. Перед началом выполнить `git fetch`, зафиксировать фактический `BASE_SHA` и не считать этот SHA актуальным автоматически.
## Ветка
`antigravity/state-layer`
---
## ГРАНИЦА РАБОТ — читать первым
Параллельно выполняется **Задание B (Codex)** — переработка интерфейса. Чтобы задания не конфликтовали, разделение проходит **по файлам**, а не по смыслу.
**Ваша зона (можно менять):**
```
src/antigravity_provider/router/state_store.py
src/antigravity_provider/router/unified_health.py
src/antigravity_provider/router/scheduler.py
src/antigravity_provider/router/event_bus.py
src/antigravity_provider/router/quota_collector.py
src/antigravity_provider/router/model_registry.py
src/antigravity_provider/router/account_identity.py
src/antigravity_provider/router/router_engine.py
src/antigravity_provider/router/router_config.py
src/antigravity_provider/router/session_affinity.py
src/antigravity_provider/router/health_tracker.py
src/antigravity_provider/router/profile_manager.py
src/antigravity_provider/router/auto_assigner.py
src/antigravity_provider/router/adapters/**
src/antigravity_provider/router/*_oauth.py
src/antigravity_provider/*.py
scripts/**, installer/**, config/**
tests/** (кроме tests/test_ui_*.py)
```
**Чужая зона (НЕ трогать):**
```
src/antigravity_provider/router/ui/** ← весь UI, включая views, components, theme, wizard
src/antigravity_provider/router/hermes_hub_app.py
tests/test_ui_*.py
```
Если для вашей задачи потребовалось изменить файл из чужой зоны — **это сигнал, что контракт спроектирован неверно**. Вместо правки UI расширьте ViewModel или событие.
---
## P0. Контракт ViewModel публикуется первым
До любых других изменений зафиксировать и запушить контракт, против которого Codex будет писать интерфейс: `docs/UI_STATE_CONTRACT.md`.
Описать точно, по фактическому коду:
- `HubSnapshot` — состав, гарантии консистентности, поле версии/seq;
- `ProfileViewModel` — все поля, какие опциональны, что означает каждое состояние `auth_state`/`health_state`;
- `QuotaSnapshot` и `QuotaBucket` — полная схема (`id`, `label`, `used_percent`, `remaining_percent`, `used`, `limit`, `reset_at`, `unit`, `scope`, `model_family`), какие поля реально заполняются **для каждого провайдера отдельно**, и признак `is_estimated`;
- `AgentViewModel`, `RolePipeline`, `PipelineNode`, `ProviderSummary`, `SystemReadiness`;
- перечень событий `event_bus` с полезной нагрузкой: что именно приходит при изменении квоты одного аккаунта, при добавлении аккаунта, при смене маршрута.
**Критично:** для каждого поля указать, реально ли backend его отдаёт, или это заглушка. Codex обязан отличать «данных нет» от «данные есть». Раздел «Backend gaps» обязателен.
Этот документ — интерфейс между двумя заданиями. После публикации менять его только с явной пометкой в отчёте.
---
## Область задачи
### 1. Единый источник состояния
Завершить `HubSnapshot` как единственный источник для UI. Ни один view не должен иметь возможности самостоятельно инициировать сканирование — не потому, что это запрещено правилом, а потому, что данные ему приходят готовыми.
Убрать вызовы `scan_all()` из кода, доступного UI: сервис отдаёт снапшот, обновляет его сам.
### 2. Централизованный планировщик обновлений
На базе существующего `HermesRefreshScheduler`:
- интервалы на провайдера;
- дедупликация одновременных запросов;
- защита от устаревшего ответа (поздний ответ не перезаписывает более свежее состояние — использовать `seq`);
- раздельные операции: обновить один аккаунт / одного провайдера / всё.
### 3. Событийная модель вместо полного пересбора
Изменение квоты одного аккаунта обязано порождать точечное событие с идентификатором аккаунта, а не сигнал «всё изменилось». То же для добавления/удаления аккаунта, смены авторизации, смены активного маршрута.
**Отдельно:** OAuth — самостоятельная state machine. Не подключать OAuth listener, таймауты авторизации и жизненный цикл PKCE к общему планировщику. После успешной авторизации: сохранить аккаунт → событие `ACCOUNT_ADDED` → точечное обновление, **без** глобального пересканирования.
### 4. Мульти-корзинные квоты
Довести `quota_collector` до реального сбора там, где провайдер это отдаёт. Требования:
- не сводить разные лимиты к одному проценту;
- корзины привязаны к семейству моделей, если у провайдера лимиты раздельные (Antigravity: Gemini и не-Gemini — проверить фактическим кодом, что именно доступно);
- каждый снапшот честно помечен `is_estimated` и источником; запрет на `*_api` для локально вычисленных значений остаётся в силе;
- если провайдер не отдаёт данных — отдавать отсутствие данных, а не число.
Роутер должен понимать, что переключение модели может сменить квотный пул.
### 5. Реестр моделей и маршрутизация
Довести `model_registry` и capability-роутинг: выбор модели с учётом возможностей, стоимости и остатка квоты. Учитывать `preferred_models` профиля и приоритет роли.
### 6. Долги, оставшиеся открытыми
- **Мёртвые модули.** `capability_matrix`, `lifecycle_supervisor`, `skill_registry` сейчас лишь реэкспортируются из `router/__init__.py` — ни один кодовый путь их не использует. Реэкспорт не равен интеграции. Либо подключить по-настоящему, либо удалить и записать причину.
- **`HKCU` в тестах.** `APPDATA`/`USERPROFILE` теперь в песочнице, но `HermesHubSetup.cs` пишет в реестр двумя вызовами `Registry.CurrentUser`, а реестр переменными окружения не перенаправляется. Вынести реальный запуск установщика в явный integration-режим либо параметризовать ключ реестра.
- **Комментарии `router_profiles.yaml`** стираются при сохранении (5 строк → 0). Оценить `ruamel.yaml`; если замена YAML-стека велика — зафиксировать отдельным долгом и не расширять scope.
- **Сериализация Antigravity.** `_AGY_INVOCATION_LOCK` вернул корректность ценой параллелизма: вызовы профилей со своим auth снова идут по одному. Оценить отказ от глобальной записи `gemini:antigravity` в пользу изоляции только через `USERPROFILE` — это снимет и лок, и гонку. Если не делать — записать как осознанный долг.
### 7. Релизная инфраструктура
- выложить ассет `hermes-hub-0.1.1.zip` с суммой из манифеста (сейчас `package_url` → HTTP 404) и `HermesHubSetup.exe` в GitHub Release;
- `dist/checksums.txt` — только из `scripts/build_dist.py`, вручную не править;
- порядок публикации: build → checksum → upload → verify → publish manifest → verify feed. Манифест не должен рекламировать несуществующий артефакт.
**Тег `v0.1.1` не создавать.**
---
## Критерии приёмки
1. `docs/UI_STATE_CONTRACT.md` опубликован до остальных изменений, содержит раздел «Backend gaps» и для каждого поля — признак реальности данных.
2. Ни один файл из чужой зоны не изменён (`git diff --name-only BASE_SHA..HEAD -- src/antigravity_provider/router/ui src/antigravity_provider/router/hermes_hub_app.py` пуст).
3. `scan_all()` недоступен из UI-слоя; сервис обновляет снапшот сам.
4. Изменение квоты одного аккаунта порождает событие с идентификатором аккаунта; тест это проверяет.
5. Поздний ответ не перезаписывает более свежее состояние; тест на гонку `seq`.
6. OAuth-сессия не управляется общим планировщиком; успешная авторизация даёт точечное обновление, тест это фиксирует.
7. Мульти-корзинные квоты: для каждого провайдера в контракте указано, какие корзины реально доступны; ни одна локально вычисленная величина не помечена источником провайдера.
8. Мёртвые модули либо интегрированы (есть вызывающий код вне `__init__.py`), либо удалены.
9. Тесты установщика не оставляют записей в `HKCU` при обычном прогоне.
10. `package_url` из манифеста отдаёт HTTP 200, sha256 совпадает с загруженным артефактом.
11. Полный `pytest` зелёный **в окружении без UI-зависимостей**; `ruff check .` чисто; release gate PASSED на финальном коммите.
12. Отчёт содержит `BASE_SHA`, `FINAL_SHA`, `origin/main`, `git status`, точный результат тестов (`X passed / Y skipped / Z failed`) и список того, что осталось.
## Порядок сдачи
Никаких функциональных коммитов после последнего запуска release gate. Ревьюеру передать точный `FINAL_COMMIT_SHA` и не продолжать разработку поверх него до вердикта.