diff --git a/agents/inbox/2026-08-21-A5-antigravity-real-telemetry.md b/agents/inbox/2026-08-21-A5-antigravity-real-telemetry.md new file mode 100644 index 0000000..9556efa --- /dev/null +++ b/agents/inbox/2026-08-21-A5-antigravity-real-telemetry.md @@ -0,0 +1,142 @@ +# Задание A5 (Antigravity): настоящая телеметрия вызовов и закрытие долгов + +## Дата поступления +2026-08-21 + +## База +Проверочный HEAD: **`31e4e7f`**, `origin/main` = `31e4e7f`. `git fetch`, зафиксировать `BASE_SHA`. + +## Ветка +`antigravity/telemetry` + +--- + +## Что принято по A4 + +Проверено исполнением: + +- **Утечка учётных данных закрыта — самый старый дефект проекта.** Подставил в окружение `OPENAI_API_KEY`, `CODEX_TOKEN_X`, `DEEPSEEK_API_KEY`, `ANTHROPIC_API_KEY`, перехватил окружение дочернего процесса `agy`: 31 переменная, **ноль ключей провайдеров**, изоляция профиля через `USERPROFILE` сохранена. +- `selection_trace` — объяснение выбора провайдера появилось в метаданных ответа. +- Изоляция реестра в тестах установщика доработана. +- Граница соблюдена. Прогон: headless 175 passed, с UI-зависимостями **222 passed**, ruff чисто, гейт PASSED. + +--- + +## Контекст: почему это задание + +Владелец сравнил установленную сборку с утверждёнными макетами. На макетах ключевое место занимают показатели: + +``` +98 ms P95 · 842 rps · 99.98% успешные · 1.23% ошибки +1.2M / 3.0M токенов · история использования в USD +CPU 28% · Память 61% · Диск 39% · Сеть 42 Мбит/с +``` + +Сейчас контракт объявляет их отсутствующими (Gap 12), и UI обязан показывать «Н/Д». **Но это верно не для всех.** Разберём по источнику данных: + +| Показатель | Источник | Вердикт | +|---|---|---| +| Латентность вызова | `router_engine.py:254-256` уже замеряет `elapsed` | **измеримо, данные выбрасываются** | +| Токены | `agy_subprocess.py:623` уже извлекает `prompt_tokens`/`completion_tokens`; HTTP-адаптеры получают `usage` от провайдера | **измеримо, данные выбрасываются** | +| Количество переключений | `failover_count` и `failover_trail` уже считаются | **измеримо, данные выбрасываются** | +| Доля ошибок | классификация ошибок уже есть | **измеримо** | +| Стоимость | нужна таблица цен по моделям | измеримо **при наличии прайса** | +| RPS провайдера, SLA, uptime | провайдеры не отдают | **неизмеримо** | +| CPU, память, диск, сеть | к работе роутера отношения не имеют | **неизмеримо и не нужно** | + +То есть четыре показателя из семи — настоящие, и мы их просто теряем. Задание: перестать их терять. + +--- + +## P0-1. Сохранять телеметрию собственных вызовов + +Сейчас `router_metadata` собирается и отдаётся в ответе, после чего пропадает. Ни одного места, где эти цифры накапливаются, в проекте нет. + +Завести хранилище телеметрии — по одной записи на вызов: + +``` +timestamp +role +profile_id +provider +model +outcome (success | failover | error | quota_exhausted | rate_limited) +latency_seconds +prompt_tokens +completion_tokens +failover_count +error_category (если применимо) +``` + +Требования: + +- запись не должна замедлять вызов — писать асинхронно либо буферизовать; +- ограниченный размер: кольцевой буфер в памяти плюс ротация файла, без неограниченного роста; +- ни одного секрета и ни одного фрагмента содержимого запроса или ответа — только метаданные; +- переживать перезапуск приложения (агрегаты можно хранить в файле рядом с `router_state.json`, атомарно, как уже сделано там). + +**Токены брать только из `usage`, который вернул провайдер.** Если провайдер `usage` не вернул — поля пустые, а не оценка по длине текста. + +## P0-2. Агрегаты, которые честно выводятся из записей + +Поверх хранилища — агрегаты по провайдеру, профилю, модели и роли за окно времени: + +- латентность: P50, P95, максимум, число вызовов; +- токены: суммарно на вход и на выход; +- переключения: сколько раз уходили с профиля и по какой причине; +- доля ошибок: доля неуспешных вызовов от общего числа. + +Это **наши собственные наблюдения за нашими же вызовами**, а не заявления провайдера. В контракте так и записать: источник `own_measurement`, а не `*_api`. Разница принципиальна и должна быть видна в данных. + +Если вызовов в окне не было — отдавать отсутствие данных, а не нули. «Ноль вызовов» и «ноль миллисекунд» — разные вещи, ровно как «нет данных» и «квота исчерпана». + +## P1-3. Стоимость — только при наличии прайса + +Стоимость считать, если в конфигурации задана таблица цен по моделям (вход/выход за миллион токенов). Нет таблицы — нет стоимости, поле пустое. + +Не зашивать цены в код: они меняются, а зашитый прайс через месяц станет тихой ложью. Формат — в конфиге пользователя, с примером в `config/`. + +## P1-4. Обновить контракт + +Gap 12 переформулировать: часть показателей переходит из «неизмеримо» в «измеряется нами». Для каждого нового поля указать источник (`own_measurement`), окно агрегации и поведение при отсутствии вызовов. Разделы «Closed Gaps» и «Active Limitations» сохранить в текущем виде — они удачные. + +RPS провайдера, SLA, uptime, CPU/память/диск/сеть остаются в «Active Limitations» с прежним требованием к UI: «Н/Д» либо блок отсутствует. + +## P1-5. Закрыть три долга + +Проверено на `31e4e7f`, всё ещё открыто и не зафиксировано как осознанный долг: + +| Долг | Состояние | +|---|---| +| `Registry.CurrentUser` в `HermesHubSetup.cs` | 2 вызова | +| `fastapi` / `uvicorn` в обязательных зависимостях | `gui_server` живёт в `legacy/` и не поставляется | +| Комментарии `router_profiles.yaml` | 5 строк → 2 при сохранении | + +По каждому — закрыть либо записать обоснование в отчёте. Третий раз переношу их из задания в задание; молчаливый перенос дальше не годится. + +--- + +## Ограничения + +- Граница прежняя: зона Codex (`router/ui/**`, `hermes_hub_app.py`, `tests/test_ui_*.py`) — не трогать. +- Никаких оценок вместо измерений. Нет данных — нет числа. +- Секреты и содержимое запросов в телеметрию не попадают. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ни один файл зоны Codex не изменён. +2. После серии вызовов (моками) в телеметрии есть записи с латентностью, токенами и исходом; проверено тестом. +3. Агрегаты считаются корректно: тест с известным набором значений проверяет P50/P95 и суммы токенов. +4. При отсутствии вызовов агрегаты отдают отсутствие данных, а не нули; проверено тестом. +5. Токены не появляются, если провайдер не вернул `usage`; проверено тестом. +6. В телеметрии нет секретов и содержимого запросов; проверено тестом с подставным ключом и текстом. +7. Размер хранилища ограничен; ротация проверена тестом. +8. Контракт обновлён: новые поля с источником `own_measurement`, неизмеримое осталось в «Active Limitations». +9. Три долга закрыты либо зафиксированы с обоснованием. +10. Прогон **в обоих окружениях**; обе команды и оба результата в отчёте. +11. `ruff check .` чисто; release gate PASSED на финальном коммите. +12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`.