WEB board

всеканоны и докиворкеры↗ iOS↗ Легаси
WEB-322 · Дефект · — · web

chat-global 503 accounting_unhealthy на КАЖДЫЙ запрос бёрста: admission-гейт видит собственное embedding-событие мид-проекции (v4-4482925)

Закрыт P0 · горит ведёт: fable-coordinator
Суть
# chat-global отдаёт 503 accounting_unhealthy при бёрсте — запрос сам себя рубит

## 2026-08-23 11:35 UTC — обнаружение и диагноз (координатор)
ФАКТ (приёмка l32accept, M4, ~11:00-11:02 локально Pi): 3-4 POST /api/chat-global подряд → КАЖДЫЙ 503 {"code":"LLM_REQUEST_REJECTED","reason":"accounting_unhealthy"}. Пользователь видит «учёт чинится, попробуйте позже».

ДИАГНОЗ по журналу+БД (read-only):
- Каждый запрос СОЗДАЛ TerminalSpendEvent (embedding-вызов ретривала, они APPLIED: tse_bf613..., tse_7167..., tse_3f0c..., tse_8457..., tse_117d... 09:59:42-10:01:38Z) — время 1-в-1 с [billing][accounting_unhealthy_rejection] в журнале.
- Гейт: accountingStateMachine.ts:328-352 ищет события, у которых ЛЮБОЙ required consumer не имеет расписки w3.9 с принятым implementationHash; evaluatePendingProjectionAdmission (:56-75) считает строку stale при createdAt>60s ИЛИ projectionState!=PENDING_ACTIVE ИЛИ lease истёк.
- Прямой повтор запроса гейта в БД ПОСЛЕ окна: 0 строк (чисто). Принятые хеши сверены с build-tree (902b8748 текущий + 2 superseded) — совпадают с расписками в БД.
- ⇒ Механика: свежесозданное событие этого же (или соседнего) запроса в момент LLM-admission ещё дописывает расписки консюмеров и находится в состоянии != PENDING_ACTIVE → мгновенный «stale» → 503. Race окна проекции, НЕ реальная порча учёта (drift=0.0000, reconcile чист: sweep=0, pending=0).

## Открытые вопросы
- Жив ли ОДИНОЧНЫЙ запрос с паузой: проба chatprobe на M4 идёт (вердикт SINGLE_OK_BURST_ONLY vs CHAT_DEAD_P0).
- Регресс ли 4482925 vs 26116cff: на 26116cff чат жил (WEB-060 закрыт живьём 09:30, owner «сейчас чат отвечает»). Подозрение на изменение порядка admission/embedding в monet-хвосте batch A — нужен дифф вызовов admission между сборками.

## Эволюция вопроса
Связки: WEB-315 (приёмка памяти заблокирована этим 503), WEB-094 (admission 500 вместо 429 — родственный класс «admission под нагрузкой»), WEB-320 (журнал). Правила: absence-of-errors-is-not-proof-of-life, fix-one-layer≠fix-the-feature.


## 2026-08-23 11:55 UTC — проба одиночных запросов: MIXED, механика уточнена (тяжесть ВЫШЕ)
CHATPROBE (M4, маркер CHATPROBE_DONE): одиночный POST №1 («Запомни маркер CHATPROBE_DONE…», 10:12:52Z) → 503 через 0.7с; POST №2 через 92с («Какой маркер…») → 200. Журнал 1-в-1: у №1 retrieval-эмбеддинг (chat-global-diag) и accounting_truth_degraded В ОДНУ СЕКУНДУ — запрос рубится СОБСТВЕННЫМ свежим embedding-событием, ещё не дописавшим расписки. №2 прошёл: эмбеддинг из кэша → нового spend-события нет → гонки нет (в журнале сразу billing_guard_request_identity, без degraded).
⇒ УТОЧНЁННАЯ ТЯЖЕСТЬ: валится не «бёрст», а ЛЮБОЙ вопрос, требующий свежего платного эмбеддинга (то есть большинство новых вопросов). Кэшированные проходят. Это объясняет и все 4 отказа приёмки l32accept.
Сырьё: qa-screens/chatprobe-single-raw.json (M4). Фикс-волна web322 уже в работе (grace-окно штатной проекции, fail-closed сохранён, 5 негативов).
Регресс-вопрос (26116cff чат жил) остаётся открытым: вероятно, в batch A embedding-вызов стал терминальным spend-событием ДО admission (порядок или новый учёт эмбеддингов) — волне web322 отразить в отчёте, если увидит по коду.


## 2026-08-23 12:50 UTC — ФИКС ГОТОВ (A1 web322, Luna xhigh, маркер WEB322_FIX_READY)
Коммит 7bf543e (ветка web322 от f3898a7). Жизненный цикл вскрыт: closeReservation создаёт event PENDING_ACTIVE (lease ~60s) в одной транзакции с фактами; projector async (queueMicrotask + cron-fallback); расписки всех консюмеров пишутся В ОДНОЙ projection-транзакции, и только потом атомарно PENDING_ACTIVE→APPLIED + очистка lease. Admission ловил окно «settlement committed, projector не закончил».
ФИКС консервативный: активное множество {PENDING_ACTIVE, APPLYING, APPLIED} допускается ТОЛЬКО при возрасте <60s И живом lease; всё остальное (PENDING_STALE/FAILED_OPEN/REPAIRED/неизвестное, возраст ≥60s, мёртвый lease) блокирует как раньше; epoch/gate/outbox/stale-dispatch/incidents НЕ ослаблены.
Тесты: фокусные 34/34 (10s+lease ПРОХОДИТ; 120s / expired lease / emergency / drift / incident БЛОКИРУЮТ; бёрст 4 событий без ложных отказов), смежный набор 110/110, scoped tsc rc=0. Full test:billing rc=1 — 9 ПРЕДСУЩЕСТВУЮЩИХ unrelated падений (в т.ч. w35Closure ждёт удалённый editor.completion.gpt4o-mini в registry) — baseline, манифест не менялся. Патч спасён (377ff1ff…).


## 2026-08-23 14:57 UTC — ЗАДЕПЛОЕНО: посадка 33 (v4-00c402c) села с 1-й попытки
rel-00c402c (SHA 58af4a94…), аттестованный cutover c НОВЫМ пин-гейтом (все 7 env-пинов сверены с аттестацией ДО флипа — негативный гейт из урока посадки 32). paidReady=true, канон проштампован. Живая приёмка фикса — следующий шаг.


## 2026-08-23 BOARDSWEEP: STALE-NOT-DEPLOYED
- Evidence: Canonical has the accounting projection/admission tests at src/lib/billing/accountingStateMachine.ts and __tests__/w38AccountingClosure.test.ts, but buildFixed is empty and the description records the fix as a later landing/candidate. No paid chat request sent; unauth POST /api/chat-global=401.
- buildFixed: <empty>; canonical: v4-081ff4fb5 / 081ff4fb5f6110f8001daec7ed910ccef2e395dc.
- Status preserved by sweep: review.
Доказательства

[2026-08-24 STALEREV против 61bc04462; в проде с посадки 45 v4-5a4edbac3] Вердикт SERVED: реализация+тесты доказаны в target tree; принято по приёмке 45-тикетного разбора. Отчёт: A1 /home/ubuntu/waves/STALEREVIEW-REPORT.md (STALEREVIEW_DONE).
Починено в
v4-5a4edbac3
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-322","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-24T14:28:59.123Z