63 lines
5.4 KiB
Markdown
63 lines
5.4 KiB
Markdown
# Отчёт: Задание A — слой состояния и данных
|
||
|
||
Дата: 2026-08-21
|
||
|
||
## Идентификаторы и границы
|
||
|
||
- Base: `f171a8069d97aef5d3a45f838daed63abf2e69c1` (актуальный `origin/main` на старте).
|
||
- Ветка: `antigravity/state-layer`.
|
||
- Контракт опубликован первым отдельным коммитом в удалённой ветке: `35881c4`.
|
||
- Тег `v0.1.1` не создавался.
|
||
- Файлы UI и `hermes_hub_app.py` не изменялись.
|
||
|
||
## Реализовано
|
||
|
||
1. `HubStateStore` и `HubSnapshot`
|
||
- snapshot имеет монотонные `generation` и `seq`;
|
||
- медленные сканирования выполняются вне блокировки store;
|
||
- поздний результат с меньшим `seq` не может перезаписать более свежий;
|
||
- дельты применяются copy-on-write, без изменения вложенных словарей frozen snapshot на месте;
|
||
- account/quota события содержат `provider`, `profile_id`, `generation` и `seq`.
|
||
|
||
2. Планировщик
|
||
- refresh одного аккаунта и одного провайдера дедуплицируется;
|
||
- quota fetch завершается до публикации account delta;
|
||
- OAuth/account events запускают обновление только соответствующего аккаунта;
|
||
- `UnifiedHealthService.refresh_profile()` пересчитывает один ViewModel без глобального scan.
|
||
|
||
3. OAuth и хранение auth
|
||
- запись `auth.json` атомарна (`temp` + `os.replace`);
|
||
- `ProfileAuthManager` публикует единое secret-free событие жизненного цикла;
|
||
- дублирующие OAuth-события и скрывающие ошибки `except: pass` удалены;
|
||
- listener, PKCE, callback и ручной fallback остаются отдельными от scheduler.
|
||
|
||
4. Квоты и routing
|
||
- baseline содержит отдельные model-family buckets и честные неизвестные значения (`None`, `status=unknown`, UI: `Н/Д`);
|
||
- runtime 429 обновляет только соответствующий аккаунт/семейство и немедленно публикует quota delta;
|
||
- `ModelRegistry` учитывает capability, cost priority и известный остаток квоты;
|
||
- exhausted pool отклоняется, неизвестная квота оценивается нейтрально.
|
||
|
||
5. Импорты и технический долг
|
||
- корневой `antigravity_provider/__init__.py` запрещает смешивание namespace package с установленной старой копией;
|
||
- добавлен runtime-тест происхождения импортов;
|
||
- неиспользуемые дубликаты `CapabilityMatrix`, `SkillRegistry` и `LifecycleSupervisor` удалены после проверки отсутствия consumers; действующие механизмы остаются в `ModelRegistry`, router policy и lease/session слоях.
|
||
|
||
## Осознанные решения
|
||
|
||
- YAML: текущий PyYAML round-trip сохраняет заголовочные комментарии, но не гарантирует inline-комментарии. Миграция на `ruamel.yaml` не включена: это отдельное изменение формата/зависимостей, не требуемое для state API.
|
||
- Antigravity: глобальный `_AGY_INVOCATION_LOCK` сохранён. Несмотря на раздельные `USERPROFILE/HOME`, CLI всё ещё использует общий Windows Credential Manager key `gemini:antigravity`; lock защищает полный swap/invoke/restore и подтверждён concurrency-тестами.
|
||
- Installer: реальные installer-тесты имеют marker `installer` и исключены из штатного pytest; HKCU не затрагивается обычным прогоном.
|
||
|
||
## Проверки
|
||
|
||
- Целевой state/data/import/routing набор: `112 passed`.
|
||
- Полный набор при установленных UI-зависимостях: `201 passed, 4 skipped, 3 deselected, 2 failed`.
|
||
- Ruff: `All checks passed`.
|
||
- Release gate: заблокирован.
|
||
|
||
Два оставшихся падения находятся в запрещённой для Task A UI-зоне:
|
||
|
||
- `test_e_repeated_open_browser_invariance`: у `AddAccountWizard` не устанавливается `oauth_port`;
|
||
- `test_f_copy_before_open_browser`: wizard распаковывает backend-результат из трёх значений как два, поэтому `oauth_url` остаётся `None`.
|
||
|
||
Backend возвращает контракт `(session_id, auth_url, port)` корректно. Исправление требуется в Task B (`router/ui/add_account_wizard.py`). До зелёного полного gate release asset не публиковался. Live manifest доступен, но package URL сейчас отвечает 404; публикация заведомо не прошедшего gate пакета сознательно не выполнялась.
|