WEB-348 · Дефект · — · web
P1 [голос]: realtime-сессия списывается по МАКСИМУМУ резерва ($2.50), а не по фактическим секундам — 3 попытки owner съели $7.50 и снесли дневной кап
Закрыт
P1 · важно
ведёт: —
Суть
# Факт (DailySpendCounter, 24.08)
feature:realtime_bootstrap costMicros=7500000 ($7.50) за 3 коротких попытки owner (секунды разговора каждая) = ровно 3 × полный резерв $2.50. Настройка manifests: settlementStrategy=terminal_event_settle_actual_or_max — сессии без корректного terminal event ложатся по max.
# Следствия
1. Кап $8/день выел за 3 попытки → сессия рвётся на пере-резерве следующего хода (обрыв микрофона после каждого вопроса), ответ доезжает текстовым фолбэком без озвучки («молчит»).
2. Экономика: короткая сессия стоит как 30-минутная.
# Что сделать
1. Terminal event для realtime-сессий: закрытие сессии обязано отправлять фактический usage (секунды) → settle-actual; разобраться, почему terminal event не долетает/не пишется (в т.ч. при обрыве браузером).
2. Пере-резерв ходов: не резервировать по $2.50 на каждый ход — либо один резерв на сессию, либо по-ходовой с actual-секундами.
3. Честная UI-ошибка при кап-стене mid-session (не молчаливый обрыв в текст-фолбэк).
4. Тест: короткая сессия N секунд → settled ≈ N×per_second, не max.
# Обход (сделан 24.08, тест-день)
SPEND_CAP_FEATURE_DAILY_USD realtime_bootstrap 8→30; SPEND_CAP_USER_DAILY_USD 10→30 (bak: .bak-testday). Вернуть к боевым значениям после починки settle-actual.
Родня: WEB-316 (хроника стен голоса), WEB-320, WEB-344. Счётчики: DailySpendCounter 2026-08-24.
Доказательства
[25.08 ~14:45] Взято волной web348 (luna xhigh, A1, wt-web348 от c3fb44e). Скоуп: корень недолетающего terminal event (штатное закрытие/обрыв браузера/сеть/кап-стена) → settle-actual с server-side страховкой по heartbeat, max только при неизвестном actual + маячок; пере-резерв ходов; честная UI-ошибка на кап-стене; тесты вкл. негатив, без живых платных вызовов. Живая проверка — после посадки по утверждённому капу таксы.
[25.08 ~16:20Z] СДЕЛАНО волной web348: commit 1810147. Архитектура settle-actual: durable VoiceSession до vendor dispatch; ОДИН резерв на сессию (ceiling, TTL+300s) вместо $2.50/ход; heartbeat клиентов (OpenAI+Google драйверы) → /api/realtime/session-usage → монотонный серверный учёт секунд; session-end с explicit usage → settleReservation по actual; РЕАПЕР по deadline/stale heartbeat (+cron /api/cron/realtime-reap) — max только когда actual принципиально неизвестен. Полный путь с файл:строками в отчёте. Scoped tsc rc=0, платные вызовы не гонялись. ⚠️ Для посадки: подключить realtime-reap в task-cron таймер на Pi. → review; в батч 54; живая проверка по капу таксы после посадки.
[25.08 ~15:00Z] В ПРОДЕ посадкой 54: релиз arm64-l59-20260825T134818Z, v4-2857a88 (merge-батч 2857a88, все проверки зелёные, Gate D сохранён ON). Таймер note-clone-realtime-reap УСТАНОВЛЕН и тикает ежеминутно ({"ok":true,"scanned":0,"closed":0} в journal). Осталось: живой replay сценария owner (3 коротких попытки → settle по секундам, не 3×$2.50) по капу таксы.
📎 Трейл: отчёт /home/ubuntu/waves/WEB348-REPORT.md; бриф web348-brief.md; ветка web348@1810147 (в проде 54, merge 5fdd3c7). Реапер: Pi /etc/systemd/system/note-clone-realtime-reap.{service,timer}. Ключевые файлы: observedSessionLedger.ts, browserSessionEnd.ts, meteredRealtimeBootstrap.ts.
[25.08 ~22:05Z] ⚠️ Независимая приёмка accept348 = NOGO, 3 блокера, из них P0 В ПРОДЕ (WEB-369): unauth session-end 200 (воспроизведено на проде). Также: terminal usage не монотонен (explicit 2s перебивает heartbeat 12.5s → занижение settle); settlement exception → completed без ретрая (reaper сканит только active); + flake 77/81. Фикс: web348fix (от 2857a88). Тесты WEB-348 РАНЕЕ приняты автором как 81/81 — оказались flaky (независимая проверка). Урок: приёмка ≠ автор. WEB-348 откат из review → in_progress.
[25.08 ~22:35Z] Все 3 блокера приёмки закрыты волной web348fix (026faa2, влит в l55 a453b16). → review (в посадку 55 после пересборки).
[25.08 ~16:30Z] В ПРОДЕ посадкой 55 (релиз arm64-l60, v4-a453b16, Gate D ON, paidReady+attest ok). web348fix влит (026faa2). Живая проверка settle-actual по капу таксы — отдельно.
[2026-08-26 00:12Z] Автор web348b: realtime списание по ФАКТИЧЕСКИМ секундам (не reservedMax), резерв освобождается; estimatorRegistry 30/30, observed realtime ledger 11/11. -> review на независимую приёмку (негатив: нет данных секунд -> fail-closed).
[2026-08-26 00:40Z] ПРИНЯТ (независимо, приёмщик≠автор): acc348 GO: realtime по фактическим секундам, estimator 30/30, ledger 11/11, reservation 39/39. → done.
Починено в
a453b16
Воркер
не привязан — привязать:
curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \
-d '{"issueId":"WEB-348","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-25T23:41:04.482Z