8.8 KiB
Задание A7 (Antigravity): два дефекта в данных дашборда
Дата поступления
2026-08-21
База
Проверочный HEAD: 69cbefc, origin/main = 69cbefc. git fetch, зафиксировать BASE_SHA.
Ветка
antigravity/dashboard-fixes
Что принято по A6
Проверено исполнением, работа хорошая:
- разрезы телеметрии в снапшоте. Прогнал 6 вызовов оркестратора и 2 исследователя (последние с переключением) — получил 10 записей, что верно: неуспешная попытка тоже учитывается. Доли сошлись точно:
openai-codex : total_calls=6, call_share=0.6
antigravity : total_calls=2, call_share=0.2
opencode-go : total_calls=2, call_share=0.2
сумма = 1.0
- разрез по ролям:
orchestrator=6,research=4— совпадает с числом попыток; - пустое окно честно:
has_data=False,latency_p50_ms=null,total_tokens=null, при этомtotal_calls=0— счётчик как факт, а латентность как отсутствие. Ровно то различие, которого добивались; - контракт снова разделён правильно: Gap 13 закрыт как самоизмеряемый, для недоступного заведён Gap 14 — RPS провайдера, SLA, очереди задач, окно обслуживания, с сохранённым требованием «Н/Д либо скрыть». Третий раз подряд без молчаливых пропаж;
- прогон: headless 189 passed, с UI-зависимостями 235 passed, ruff чисто, гейт PASSED.
P0-1. active_calls всегда ноль: два разных LeaseManager
state_store.py:172 читает синглтон:
active_leases_total = LeaseManager.get().total_active_count()
router_engine.py:31 создаёт собственный экземпляр:
self.leases = leases or LeaseManager()
Роутер захватывает лизы в своём приватном менеджере, снапшот читает синглтон, который всегда пуст. Проверено:
движок и синглтон — один объект? False
id(engine.leases) = 2732720631632
id(LeaseManager.get()) = 2732720631376
после acquire на движке:
движок видит : 1
синглтон видит: 0
Итог: active_calls_total в снапшоте будет постоянно 0 при любой нагрузке. Блок «Активные задачи» на дашборде покажет ноль всегда — то есть правдоподобное, но неверное число.
Требуется: один источник истины по лизам. Либо RouterEngine по умолчанию берёт синглтон, либо state_store читает лизы у движка. Второе честнее: лизы принадлежат движку, а не глобальному состоянию.
Тест обязателен: захватить лиз через тот же путь, которым пользуется роутер, и убедиться, что снапшот его видит.
Попутно: ProfileHealthRecord.active_leases по-прежнему всегда 0 и при этом выводится в CLI и API — это отмечалось ещё в первом аудите. Либо заполнять, либо убрать из вывода.
P0-2. CPU при первом измерении всегда 0 %
host_metrics.py:57 — psutil.cpu_percent(interval=None). Без предыдущей точки отсчёта первый вызов возвращает 0.0 по устройству самой библиотеки. Проверено:
первый вызов (холодный): 0.0
второй вызов: 44.1
третий вызов: 12.0
На старте приложения пользователь увидит «CPU 0 %» — и это тот же класс дефекта, с которым мы боролись в квотах: число выглядит измеренным, но измерением не является.
Требуется одно из: прогреть счётчик при инициализации сервиса (один вызов, результат отбросить), либо возвращать None для первого измерения, либо использовать короткий интервал. Первый вариант предпочтительнее — он не создаёт «дырки» в интерфейсе.
Тест обязателен: первое значение не должно быть нулём, полученным из-за холодного старта.
P1-3. Остаток по YAML
Комментарии сохраняются только заголовочные, внутренние теряются:
# Antigravity (10 accounts)
# OpenAI Codex (3 accounts)
# OpenCode Go (3 accounts)
# Optional: User Model Pricing Table (USD per 1M tokens)
# Telemetry will compute call cost in USD only if a model price is defined below.
Первыми стираются пояснения к таблице цен, добавленные в A5. В отчёте статус указан «Закрыт» — это неточно. Либо довести до полного round-trip, либо переписать статус на «частично» с перечнем теряемого.
P1-4. «Сеть» показывает счётчик, а не текущий показатель
host_metrics.py:79-80 отдаёт net_bytes_sent / net_bytes_recv — накопительные счётчики psutil.net_io_counters() с момента загрузки машины. dashboard_view.py:630-633 складывает их и делит на 1024², получая «Сеть 56155 МБ».
Это число стоит в одном ряду с CPU, памятью и диском — то есть читается как текущее состояние, а на деле это ~55 ГБ трафика за всё время работы системы. На макете в этой позиции была скорость («42 Мбит/с»).
Число настоящее, вводит в заблуждение подача. Нужно одно из двух: считать скорость по разнице двух замеров (это и просил макет) либо отдавать значение с явной семантикой «всего с момента загрузки», чтобы интерфейс мог подписать его правильно.
Отрисовка — зона Codex, но решение о том, что именно отдавать, принимается здесь: сейчас в контракте net_bytes_* описаны без указания, что это накопительный счётчик.
Ограничения
- Граница прежняя: зона Codex (
router/ui/**,hermes_hub_app.py,tests/test_ui_*.py) — не трогать. - Никаких чисел без измерения. Ноль допустим только как результат подсчёта, а не как значение по умолчанию.
- Тег
v0.1.1не создавать.
Критерии приёмки
- Ни один файл зоны Codex не изменён.
- Захваченный роутером лиз виден в снапшоте; проверено тестом через реальный путь захвата.
ProfileHealthRecord.active_leasesзаполняется либо убран из вывода CLI и API.- Первое измерение CPU не равно нулю из-за холодного старта; проверено тестом.
- Статус YAML в отчёте и контракте соответствует фактическому поведению.
- Прогон в обоих окружениях; обе команды и оба результата в отчёте.
ruff check .чисто; release gate PASSED на финальном коммите.- Отчёт:
START_HEAD,FINAL_HEAD,origin/main,git status, точныйX passed / Y skipped / Z failed.
Порядок сдачи
Передать точный FINAL_COMMIT_SHA.