# Путь денег — от гранта до settle (топикальный вход; строка 167 ЗАКРЫТА D39.200, файл живёт сам по себе) > **Статус: топикальный ВХОД, НЕ источник истины.** Док собирает денежную тему, размазанную по D-нотам, реестру дефектов платформы, глоссарию и бэклогу, в один маршрут чтения. При конфликте с чем угодно побеждает `docs/architecture/05-decisions-log.md` (правило чтения — диета D39.125: живой файл + слайсы). Каждое утверждение здесь снабжено указателем на первичный носитель; формулы — дословно оттуда. Словарь терминов (холд · прирост · аргумент потолка · baseline · settle · leftover-reserved · SpendBound) — `docs/glossary.md` §«Деньги платформы» (`docs/glossary.md:58`=`**Деньги платформы:**`). > > ⚠ **Форма указателей — норма этого дока (31.08, пере-проверены ВСЕ указатели: 34 уехали, 1 мёртв, 16 верны).** > **Стабильна не ЗОНА, а ЧИСТЫЙ ФАЙЛ.** Номер строки ставится только туда, где `git status <цель>` показывает файл чистым; файл с незакоммиченной правкой — чей угодно, включая `docs/` — получает греп-указатель без номера. Ось именно такая — ратифицировано **D39.179 п.4**, который уточняет D39.177 §2в и прямо объявляет зонную ось НЕВЕРНОЙ (там же: «Носитель нормы — шапка 15-money-path.md»), и зонная ось её НЕ заменяет: `docs/architecture/14-api-contract/openapi.yaml` ЛЕЖАЛ в «своей» зоне и при этом ДВИГАЛСЯ паком контракта 0.9.0 (заленджен 31.08, `0a680a3`, D39.180). > Токен-форма — `` `путь:N`=`ожидаемая подстрока` ``; её гейт `docs/scripts/counts.py --lint` сверяет ПО СОДЕРЖИМОМУ и краснеет, когда цель уезжает. > ⚠ **Что гейт берёт с якоря БЕЗ токена:** существование файла и что номер не больше длины файла; у голого имени журнала решений — ещё и структуру (владелец строки обязан совпасть с названной рядом нотой). НЕ проверяется только СОДЕРЖИМОЕ — вот почему 34 якоря уехали молча. Голое имя без «/» не судится вовсе. > ⚠ **Греп-форма не проверяется гейтом никак** (регексп якоря требует `:номер`) — осознанный размен: переживает чужой WIP, но молчит. > ⚠ **Токен НЕ должен содержать бэктик:** регексп берёт токен как «всё до следующего бэктика» и обрезает хвост, а необрезанная часть не проверяется вовсе. ## 1. Канон одним абзацем Деньги видимы с первого вызова: телеметрия, потолки, явное согласие на пере-оплату (CLAUDE.md, цель 5). Леджер без потолка запрещён — Р7: книга обязана нести хотя бы один из `book_usd`/`day_usd` (`backend/internal/config/book.go:332`=`a ledger with no ceiling is forbidden`, текст валидатора: «a ledger with no ceiling is forbidden, Р7»). ⚠ **ИСПР. 05.09 — ЗАПРЕТ ОТОЗВАН ВЛАДЕЛЬЦЕМ (D39.196, эррата 05.09-а): баланс аккаунта, потолок заказа и холд ВЫХОДЯТ наружу деньгами;** запрещённым осталось другое и оно не отменялось — цены моделей, стоимость стадий и вызовов, структура НАШИХ расходов (ПТ-33). Ниже — прежняя редакция, она объясняет, ОТКУДА запрет взялся и почему его половина жива: суммы на пользовательский экран НЕ выходят — запрет D39.84, подтверждён D39.100/К-8 (дословно: «страница лимитов/использования в настройках (СТАТУС, не суммы — запрет денег на экране D39.84 в силе и НЕ superseded)» — `docs/architecture/05-decisions-log.md`, греп `^## D39.100` → пункт К-8, дословная подстрока `СТАТУС, не суммы — запрет денег на экране`; ⚠ адресуем НОМЕРОМ ноты, а не строкой журнала) и держится в контракте (`docs/architecture/14-api-contract/openapi.yaml`, греп `State of the credit balance. No window` — ⚠ номер не ставим: контракт переверстывается минорами, греп-форма дешевле поддержки). Защита от перерасхода двойная и не зависит от событий: холд платформы делает кредит недоступным следующему прогону, а движок сам останавливается на потолке — «an overspend is impossible even while the platform is blind. The event stream is freshness only» (док-коммент `Hold` — `platform/internal/pgstore/credits.go`, греп `an overspend is impossible even`). ## 2. Деньги ДВИЖКА (зона `backend/`) - **Леджер SQLite.** Таблица `spend`, две фигуры: `committed_usd` (потрачено, с сырым ответом провайдера) и `reserved_usd` (зарезервировано под летящий вызов). `Reserve` перед каждым платным вызовом сверяет `SUM(committed_usd + reserved_usd)` книги и дня с потолками (`backend/internal/store/ledger.go:44,58,63`=`func (s *Store) Reserve(`; Р7 — коммент `backend/internal/store/ledger.go:12`=`per-day (Р7); checked against`); `SettleWithCheckpoint` одной транзакцией конвертирует резервацию в committed и персистит ответ (`backend/internal/store/ledger.go:178`=`func (s *Store) SettleWithCheckpoint(`). ⚠ У ДВИЖКОВОГО `store.Store` метода `Settle` нет — он называется `SettleWithCheckpoint` (словарь кода: `backend/internal/store/ledger.go:11`=`Reserve/Settle/ReleaseReservation semantics`). ⚠ Не спутать с ПЛАТФОРМЕННЫМ `credits.Settle` (§3 п.8) — это другой `Store`. Суммарно по книге — `SpentUSD` (`backend/internal/store/ledger.go:400`=`SpentUSD reports (committed, reserved)`). - **Гейт потолков — ПЕР-ВЫЗОВНЫЙ** (`Reserve` на каждый свежий attempt). На границе юнита сидел РЕПЭЙР-суб-бюджет — ужесточён до пер-вызовного с ценой вызова (строка 135 закрыта D39.131); эскалационный кап хоп НЕ прицениваает — перелёт ≤1 хопа, задокументирован и запинен (диспозиция D39.131 п.2д, реопен — живой инцидент). ⚠ **ТРЕТЬЕ семейство, которого перечень не знал (доп. ревизией 02.09): банк-роли несут СОБСТВЕННЫЕ КНИГО-ШИРОКИЕ бюджеты** — `gates.terminology.budget_usd` и `gates.terminology.classify_budget_usd`. Именно оно резало ОПЛАЧЕННУЮ работу на холодном прогоне 31.08 (D39.182 §4: инцидент был на классификаторе). С 31.08 (D39.182) план прохода режется ценой партии ДО первого вызова, а не обрывается посередине; усечение ВИДНО в отчёте — поля `BatchesDropped` и `ClassifyBatchesDropped` (`backend/internal/pipeline/terminologist.go`, греп `BatchesDropped`). ⚠⚠ **И сами цифры этих суб-бюджетов в книжных конфигах КАЛИБРОВАНЫ ПОД ИЮЛЬСКИЕ ЦЕНЫ** (тот же множитель ×4.47, D39.179 п.1): на холодном прогоне 31.08 `classify_budget_usd` 0.02 оборвал классификатор дважды, а поднятый до 0.08 `escalation.budget_usd` был пробит фактом до 0.103305. То есть суб-бюджеты режут ОПЛАЧЕННУЮ работу не по замыслу, а по протухшей калибровке. - **leftover-reserved зануляется write-open.** `store.Open` (путь записи, каждый `translate`) выполняет `recoverReservations` (`backend/internal/store/store.go:110`=`s.recoverReservations(ctx)`; сама функция — `backend/internal/store/store.go:278-279`=`UPDATE spend SET reserved_usd = 0`) — файл владеется одним процессом, значит любой reserved на открытии принадлежит несеттлённому прогону. `OpenReadOnly` этого прохода намеренно НЕ делает (`backend/internal/store/store.go:124`=`does not run that pass`) ⇒ **reserved, увиденный read-only `status` В МОМЕНТ СПАВНА, — остаток мёртвого процесса** (несущий факт формулы PD-158, см. §3). ⚠ Но НЕ «всегда»: `OpenReadOnly` построен ровно затем, чтобы `status` работал ВО ВРЕМЯ живого прогона (`backend/internal/store/store.go:122`=`allows concurrent readers while a writer is live`), и конкурентный `status` покажет ЖИВУЮ резервацию между `Reserve` и settle. Узко формулирует и сам код: «after a run crashes, reserved_usd stays non-zero until the next WRITE command» (`backend/internal/store/store.go:130`=`after a run crashes, reserved_usd stays non-zero`), и платформа — «at spawn there is no other writer … so anything reserved is by construction a leftover, never a live promise» (`platform/internal/runs/spawn.go`, греп `never a live promise`). - **`--max-units` — ОБЪЁМНЫЙ потолок прогона, ортогональный денежному** (D39.165 §1б, принят D39.170). Ограничивает не деньги, а РАБОТУ: не больше N выходных ЮНИТОВ (гранулярность `units_total` манифеста — та же, в которой платформа продаёт главы) возьмут СЛОТ гранта в этом прогоне; юниты, отданные за $0 (резюм, ре-пин), ретраи и эскалации внутри юнита потолок не тратят. ⚠ **«Слот» ≠ «оплата», и с 03.09 это РАЗНЫЕ числа** (пак «число согласия», строка бэклога 232): юнит, который прежний прогон НАЧАЛ и не отгрузил, дописывается ВНЕ гранта, поэтому грант N оплачивает ДО 2N выходных юнитов — замерено приёмкой на живом раннере (грант 2 → `Paid()=4`, шесть вызовов провайдера). Единственный денежный бонд здесь — `--ceiling-usd`. ⛔ **Семантика переноса НЕ ратифицирована — слово владельца 03.09 «подумаем на этот счёт»**, и до его решения читать это как замеренный факт, а не как норму. Принимает только `translate`. **Остановка по объёму — ЗАВЕРШЕНИЕ (exit 0), не пауза:** словарь кодов выхода не расширялся и нового значения `Finished.Outcome` тоже нет — ⚠ **но с 31.08 признак едет ЧИСЛАМИ в кадре `finished` шва, а не только прозой** (D39.181 п.2, закон раскрытия: прозаическая строка отчёта до потребителя потока не доезжала): носитель — `Finished.Volume`, леджер доставки. Различение при этом живёт и в отчёте прогона, и в логе; отчёт разводит ДОСТАВКУ и ПЕРЕ-ДЕЛКУ. Носитель — `backend/internal/pipeline/volume.go:13`=`the VOLUME ceiling — the run's second stop`; словарь флага дословно — `backend/cmd/tmctl/invocation.go:143`=`Stopping on it is a COMPLETION (exit 0), not a pause`. ⚠ Проводка в платформу ГЕЙЧЕНА (`PD-422`): единственный писатель признака движения банка — дверь правок, рост АВТО-банка от майнинга флага не ставит. - **`--ceiling-usd` — КНИЖНЫЙ потолок, не бюджет прогона.** Дословно из флага: «the book USD ceiling in force for THIS RUN ONLY — it OVERRIDES book.yaml `ceilings.book_usd` and is never written back. It caps the book's CUMULATIVE committed+reserved spend, not this run's increment…» (`backend/cmd/tmctl/invocation.go:142`=`the book USD ceiling in force for THIS RUN ONLY`; ⚠-коммент `backend/cmd/tmctl/main.go:261`=`--ceiling-usd is NOT a per-run budget`; многоточие закрывает обрыв цитаты — во флаге дальше стоит «, and must be > 0»). Ратификация — D39.122 п.2(в): пересчёт «пользовательский прирост → абсолют» — обязанность ПЛАТФОРМЫ, вторая денежная ось не заводится. День-потолок флаг НЕ перекрывает (PD-157). Проводка внутри: `Ceilings.BookUSD` в `Reserve` и именование сработавшего потолка в ошибке — `backend/internal/pipeline/stagerun.go:629`=`BookUSD: r.bookCeilingUSD()` и `backend/internal/pipeline/stagerun.go:716`=`overrides the book's ceilings.book_usd` (стоп = `errReserveCeiling`, `backend/internal/pipeline/escalation.go:49`=`var errReserveCeiling`). - **Потолки — wiring, не семантика:** `Ceilings` намеренно исключены из `BriefHash` («Wiring fields (paths, ceilings, db) deliberately excluded», `backend/internal/config/book.go:345`=`Wiring fields (paths, ceilings, db) deliberately excluded`; канон — `backend/internal/config/book.go:369`=`canon := struct {`) ⇒ смена ДЕНЕЖНОГО потолка не двигает ни снапшот, ни ре-билл (D39.110 п.2б). ⚠ Верно ровно про ДЕНЬГИ: потолок СЕГМЕНТАЦИИ `edit_ceiling_out` в снапшот ВХОДИТ и меняет границы чанков (`backend/internal/pipeline/snapshot.go:34`=`EditCeilingOut int`). - **`status --json` отдаёт фигуры платформе:** `committed_usd`, `reserved_usd`, `book_ceiling_usd`, `ceiling_pct` = 100·(committed+reserved)/book_ceiling (`backend/internal/pipeline/status.go:342`=`book_ceiling_usd,omitempty`; арифметика `ceiling_pct` — `backend/internal/pipeline/status.go:949`=`100 * (committed + reserved)`). Дневной фигуры в status нет (PD-157 — `platform/docs/DEFECT_REGISTER.md`, греп `PD-157`). - **Леджер = НИЖНЯЯ граница; строка 78 снята с трекера 10.09, но её ЖИВОЙ ОСТАТОК несёт строка 377.** ⛔ **Испр. 10.09 — прежняя редакция этого пункта, написанная в тот же день, была НЕВЕРНА, и ошибка моя.** Она утверждала, будто оплаченный `2xx` с нечитаемым телом «прежде в `request_log` не попадал» и что это починил пак оборванных вызовов. Замер: ветвь `BilledDecodeError` писала свою строку с пометкой оценки ещё до пака — на `3f05fab^` это `stagerun.go:629`, введено коммитом `2f91b04` **24.07**, а пак этой ветви не касался вовсе (хитов `Estimated` в его диффе по `stagerun.go` — **0**). И носитель я процитировал ЧУЖОЙ: `backend/internal/pipeline/cutcall.go:133`=`rl := r.baseRequestLog(c.stage, c.chunk` обслуживает `*llm.AttemptCutError` (`backend/internal/pipeline/stagerun.go:802`=`var cut *llm.AttemptCutError` — ⚠ испр. 16.09: было :741), а речь шла о `*llm.BilledDecodeError` (`stagerun.go:709`) — это разные классы, и второй до `cutcall.go` не доходит. **Что пак действительно купил:** строку `request_log` за вызов, который оборвали МЫ (прежде он падал в ветвь «ничего не куплено» и получал $0-строку) — вот это `cutcall.go:133`. **Что остаётся правдой:** леджер — нижняя граница, потому что обрыв, за которым ретрай УСПЕЛ, исчезает бесследно (строка **377**). Живой замер `D39.86` — 3 вызова из 14, неизвестность $0.015111 при леджере $0.114378. - **ГРАНИЦА ДЕНЕГ ЗА ОБОРВАННЫЙ НАМИ ВЫЗОВ — ФАКТ ДОСТАВКИ, а не статус-строка** (пак строки 360, акт `D39.232`; слово владельца — `D39.230`). Деньги берутся, только когда провайдер подтвердил запрос ответным объектом: `backend/internal/llm/attemptcut.go:221`=`Billable: answered && tr.afterHeaders()`. Наш собственный дедлайн даёт свой класс `attempt_timeout` без ретрая; дедлайн вычисляется ИЗ БЮДЖЕТА вызова, а не назначается (`backend/internal/llm/attemptcut.go:269`=`func (p RetryProfile) deriveDeadline`), так что эскалация, удвоившая `max_tokens`, удваивает и время. Оплаченный обрыв получает чекпойнт, и резюм его НЕ перепокупает. ⛔ Пометка «это оценка» — УСЛОВИЕ слова владельца (`D39.230` п.1) — сегодня в движке живёт ТРЕМЯ носителями (колонка `Estimated` в `request_log` — `backend/internal/store/requestlog.go:41`=`Estimated marks a row whose CostUSD`; пара `estimated_rows`/`estimated_usd` в `status --json` — `backend/internal/pipeline/status.go:911`=`Re-payment projection (spec`; печать оператору — `backend/cmd/tmctl/render.go:402`=`the degraded/err tail`), но НАРУЖУ не выходит ни одним каналом шва: строка **382**. ⚠ Формулировка испр. 10.09 — прежняя («живёт только в `status --json`») звала следующего чинить пометку, которая уже есть. - **Согласие на пере-оплату — ДВИЖКОВЫЙ гейт**. Прогон, который пере-покупает уже оплаченные юниты дороже порога, ОСТАНАВЛИВАЕТСЯ и требует явного согласия. Порог по умолчанию ратифицирован D20.2-Q2: `min($0.50, 5% × ProjectedBookUSD)`. Носитель — `backend/internal/pipeline/rebill.go`, греп `rebillConsentThreshold`; единственная ручка книги — `rebill_consent_usd` (валидатор `backend/internal/config/book.go`, греп `rebill_consent_usd`; 0 = ратифицированный дефолт). ⚠ Порог считается БЕЗ контура банк-ролей (D39.182 §3), то есть срабатывает позже, чем подсказывает полная проекция. ## 3. Деньги ПЛАТФОРМЫ по шагам (зона `platform/`) 1. **Грант.** `Grant` — строка леджера `kind='grant'`, идемпотентная по `(аккаунт, source, source_id)`: `on conflict (user_id, source, source_id) do nothing` (`platform/internal/pgstore/credits.go`, греп `func (s *Store) Grant(`; сама идемпотентность после пака sqlc уехала В ЗАПРОС — `platform/internal/pgstore/queries/credits.sql`, греп `on conflict (user_id, source, source_id) do nothing`). Фри-тир — тот же механизм в ТОЙ ЖЕ транзакции, что создание аккаунта (`platform/internal/pgstore/identity.go`, греп `appendLedger(ctx, tx, userID, "grant"`; ⚠ **дефолт `SignupGrantMicroUSD` — НОЛЬ** (`platform/internal/config/config.go`, греп `SignupGrantMicroUSD:`), а НЕ «5 × 1_000_000» — эта сумма ходила по докам и ложна: предложение «на бете дефолт в НОЛЬ» (D39.110 п.3) ИСПОЛНЕНО, `PD-104` в регистре стоит `fixed(P7, дерево сессии)`). 2. ⚠⚠ **ИСПР. 06.09 — ШКАЛЫ В ГЛАВАХ БОЛЬШЕ НЕТ (лендинг формы заказа, `D39.208`).** Объём заказывают ТРЕМЯ формами: вся книга (дефолт) · до главы N · знаками; ползунок — ДЕНЕЖНЫЙ (`limit.min/max_micro_usd`, минимум = неделимый шаг движка, и он может быть БОЛЬШЕ максимума, когда баланс не несёт даже одной резервации). Ниже — прежняя редакция, историей: **Выбор потолка — шкала в ГЛАВАХ** (D39.110: от 1 до максимума, ноль не выбираем; потолок принадлежит ПРОГОНУ, в `book.yaml` не пишется). **Максимум = Balance КАК ЕСТЬ** (D39.115 п.2а; эррата в шапке D-лога — `docs/architecture/05-decisions-log.md:4`=`Balance КАК ЕСТЬ`): `Hold` — дебет в момент взятия, баланс уже не содержит открытых холдов, повторное вычитание вдвое укорачивает шкалу. Реализация: `pricing.Model.Scale` — `affordable = balance / PerChapter`, кламп по остатку глав, дефолт = верх шкалы (ратифицировано D39.123 п.2е; компромисс «весь баланс под одну книгу» принят осознанно) — `platform/internal/pricing/pricing.go`, греп `func (m Model) Scale(`. 3. ⛔⛔ **СТАВКИ ЗА ГЛАВУ БОЛЬШЕ НЕ СУЩЕСТВУЕТ — ни константы, ни ручки (испр. 06.09).** Цена берётся из ПРОЕКЦИИ ДВИЖКА (`expected_usd` · `book_once_usd` · `step_max_usd` в манифесте), холд считается `k × ожидаемое + шаг [+ книжный бонд]`. `PerChapterMicroUSD` удалён целиком; от `DefaultPerChapter` в дереве остался ОДИН хит — надгробный комментарий `platform/internal/pricing/pricing.go` («Until 05.09 a chapter was worth a CONSTANT»), и грепать его как живой носитель нельзя. Прежняя редакция историей: **Ставка $0.03/глава — конфиг-константа платформы:** `DefaultPerChapter = 30_000` micro-USD (`platform/internal/pricing/pricing.go`, греп `DefaultPerChapter`), переопределяется `PerChapterMicroUSD` (`platform/internal/config/config.go`, греп `PerChapterMicroUSD`). ⚠⚠ **ЧИСЛА НИЖЕ — ИЮЛЬСКИЕ ДЕНЬГИ И С 16.08 НЕ ДЕЙСТВУЮТ (испр. ревизией 02.09).** D39.179 п.1 ратифицировал: DeepSeek пере-пинен под цены, вступившие **16.08 16:00 UTC**, множитель к июльским деньгам **×4.47**. ⇒ по действующему прайсу та же выборка даёт среднюю главу **≈$0.051–$0.085** и максимум **≈$0.168**, то есть константа $0.03 не завышена, а **ЗАНИЖЕНА**, и знак вывода перевернулся. ⚠ Иллюстрация живыми деньгами (НЕ калибровка): холодный прогон 31.08 дал полную цепь главы $0.093 и $0.151 при `projected_book_usd = 1.1377277707` — но это **n=2 главы из десяти**, прогон остановлен снапшот-гардом, промпт черновика был банкнотным вместо конвенционного, и сам отчёт запрещает такое употребление дословно (`archive/reports/COLDRUN_V16_REPORT_2026-08-31.md`, греп `калибровать ставку платформы`). **Провенанс ниже — ИСТОРИЯ (D39.165 §1, 28.08):** средняя **$0.0115–$0.0190**, максимум одной главы $0.0375, «завышена в **1.6–2.6×**» — ⚠ но НЕ покрывает максимум: источник (D39.165 §1) добавляет «и отдельные главы её уже пробивают» ($0.0375 против $0.03 = ×1.25), и «купить 10 глав» отдаёт движку $0.30, покупающий ~16–26 глав («денежная ручка с подписью „главы"», `PD-410`). РЕШЕНО: цена — от ОБЪЁМА ИСХОДНИКА (носитель `chapters.units_total`, пишется интейком за $0), настоящий стоп — объёмный потолок В ДВИЖКЕ (см. §2). ⛔ Константу по сегодняшним числам НЕ калибровать (D39.165 ⛔г) — ⚠ **испр. 05.09: гейт ПАЛ вместе со строкой 202** — живой платный прогон отработал 04.09 и показал, что ставка занижена ×4.47; строка 202 закрыта D39.200. Калибровка ставки идёт заказом строки **281** (⚠ испр.: строка 278 ЗАКРЫТА лендингом `81a89e9` и снята — проекция цены построена и опубликована) (проекция цены и замер стоимости тысячи знаков), а не запретом. Исторический провенанс: exp08 v2 $0.0219/глава → ревизия D30.4 +15–25% → округление ВВЕРХ (низкая ставка опаснее высокой — прогон встанет на потолке посреди книги; `platform/internal/pricing/pricing.go`, греп `Provenance, so the number is auditable`). ⚠ **испр. 05.09: движковая половина ПОСТРОЕНА и опубликована** — `expected_usd` по главам, `book_once_usd`, `step_max_usd` в `manifest --json`/`status --json` (лендинг `81a89e9`, акт D39.206). Непостроенной осталась ПЛАТФОРМЕННАЯ половина: `git grep -lE "StepMax|step_max" -- platform/` пусто, то есть числа публикуются и никем не читаются. 4. **Холд прироста ДО спавна.** `holdTx` пишет отрицательную строку `('hold', -amount)` и тем же знаком двигает кэш баланса (`platform/internal/pgstore/credits.go`, греп `func holdTx(` — строку леджера пишет `appendLedger(… "hold", -amount …)`, кэш баланса двигает `MoveBalance` внутри неё); нехватка — `ErrInsufficientCredit`. Холд + прогон + попытка + очередь — одна транзакция (`platform/internal/pgstore/runs.go`, греп `func (s *Store) StartRun(`; рестарт — греп `func (s *Store) RestartRun(`; форма D39.123 п.2д). 5. ⚠ **ИСПР. 06.09: формула ЖИВА дословно, сменился ИСТОЧНИК прироста.** `return m.committed + increment` стоит на месте (`platform/internal/runs/spawn.go`, греп `func (m meter) bookCap`), но `increment` приходит теперь из проекции движка, а не из «глав × ставку». Прежняя редакция историей: **Аргумент движку: `bookCap = committed + прирост×ставка`, БЕЗ reserved — PD-158, дословно из кода: `return m.committed + increment`** (`platform/internal/runs/spawn.go`, греп `return m.committed + increment`). Обе фигуры читаются ОДНИМ вызовом `status --json` перед стартом (`bookMeter` — `platform/internal/runs/spawn.go`, греп `func (s *Service) bookMeter(`; отказы — греп `Absent is not zero`); отсутствие любой из них = отказ спавна («absent is not zero», PD-40). Почему без reserved: write-open движка занулит leftover до первой резервации, значит цифра из read-only status к моменту сравнения уже мертва; ратифицированная ранее формула с reserved переплачивала запасом ровно на leftover сверх холда — исполнено обеими формулами против гейта (D39.123 п.2б, поправка к D39.122(в)). Аргумент ≠ холд: он больше холда на committed (`docs/glossary.md:58`=`ЭТО НЕ ХОЛД — он больше холда`). Фактически ушедшее значение хранится в `run_attempts.ceiling_arg_micro_usd` (PD-144, миграция 00011); потолок уходит argv-шаблоном `{{usd}}` (шаблон — `platform/internal/config/config.go`, греп `CeilingArg`; подстановку делает `platform/internal/runner/engine.go`, греп `{{usd}}`; подстановку и рендер argv делает `platform/internal/runner/engine.go`, греп `func (t CeilingTemplate) Args(`; `platform/internal/runs/runs.go`, греп `func (s *Service) ceilingFor(` — однострочная делегация к нему; `spawn.go` потолок только передаёт); строка 145 закрыта D39.122. 6. **Baseline и списание.** `baseline = committed` книги на старте попытки (`platform/internal/runs/spawn.go`, греп `baseline, bookCap := m.committed`); списание попытки = разница счётчика с baseline; счётчик ниже baseline = подменённая БД, сеттлится в ноль с WARN — сами цифры в лог не идут (D39.84; `platform/internal/runs/reconcile.go`, греп `the book's meter reads below`). 7. ⚠ **Терминальный вердикт ОПЕРАТОРА — путь холда мимо settle (добавлено 22.08, P8-FIX/D39.154).** Прогон, чей движок не ответит уже никогда, закрывает человек: `tmplatformctl run abandon --run --reason <текст> [--release-hold]`. Команда ОТКАЗЫВАЕТ, если попытка ещё называет юнит или несёт базовую линию траты — над живым движком прогон не закрывают. Холд возвращается ЦЕЛИКОМ в обоих случаях (к abandon допускается только попытка, не дошедшая до движка, значит она ничего не потратила), а флаг решает лишь КОГДА — сразу или на ближайшем свипе. ⚠ **С пака P11 (`PD-385`) ВЕТВЕЙ ДВЕ, и абзац выше — только ЖИВАЯ** (испр. ревизией 02.09; образец — секция «Строка PHASE = settling» в `platform/deploy/README.md`). У прогона, чья попытка уже КОНЧИЛАСЬ, команда идёт второй ветвью: отказ «юнит/базовая линия» к ней НЕ применяется, к abandon допускается попытка, которая ДОШЛА до движка и ПОТРАТИЛА, холд всё равно возвращается ЦЕЛИКОМ по каждой осиротевшей попытке, а флаг `--release-hold` там ИГНОРИРУЕТСЯ; единственная защита — порог `abandonAfter` неудач расчёта (отказ `ErrSettlementNotStuck`). ⚠ **Ветвление идёт по `runs.finished_at`, а НЕ по наличию осиротевшей попытки** — ратифицированная оговорка `PD-418`. ⚠ **И вторая ветвь НЕ лечит живую:** прогон с намертво заблокированной расплатой уходит в ЖИВУЮ ветвь и закрыт отказом по юниту до сих пор — `PD-424` стоит `open` именно на этой половине. 8. **Settle по фигуре движка.** `reconcile.settle` (`platform/internal/runs/reconcile.go`, греп `func (s *Service) settle(` — ⚠ номера строк в файлы живой зоны не ставим, пока её сессия работает) читает committed из status; `credits.Settle` одной транзакцией закрывает резервацию, возвращает холд и списывает spent; **spent > холда — капится холдом** с явной пометкой «capped at the hold; the engine reported …» (`platform/internal/pgstore/credits.go`, греп `capped at the hold; the engine reported`), перерасход движка счётом не абсорбируется. 9. **SpendBound (PD-159).** Отложенный settle читает пожизненный счётчик КНИГИ и без границы оплатил бы работу прогона-преемника (замер: $2.10 за прогон ценой $0.10, и преемник платил снова). Граница = наименьший baseline попыток той же книги, стартовавших ПОЗЖЕ: `committed = min(счётчик сейчас, SpendBound)` (`platform/internal/runs/reconcile.go`, греп `bound != nil`; `platform/internal/pgstore/runs.go`, греп `func (s *Store) SpendBound(`). 10. **Открытое — эскроу/intent (строка 136 бэклога, `docs/BACKLOG.md`, греп `Write-ahead intent`):** write-ahead intent + состояние `uncertain`-эскроу + `closing`; холд НЕ освобождается по выходу юнита (сигнал обгоняет денежные события); spend не постится без свидетельства провайдера; расход сверх холда — полной суммой, превышение явной строкой убытка. Стройка по research/25, «скоро». ⚠ **«Строка 103» — ФАНТОМ, снят 31.08:** такой строки в бэклоге нет (закрыта D39.131), а эмиттер шва ПОСТРОЕН с обеих сторон — платформа тейлит `events.jsonl` и принимает кадр `spend` (`platform/internal/ingest/events.go`, греп `TypeSpend Type`). Денежной слепоты это НЕ снимает по построению: поток событий — только свежесть. Платформа между спавном и settle слепа — защита держится холдом и потолком движка (§1). ## 4. Фронт (зона `frontend/`) Максимум шкалы — `Balance` КАК ЕСТЬ (D39.115 п.2а); `Reserved` — справочная величина, `ReadAccount` отдаёт обе раздельно (`platform/internal/pgstore/credits.go`, греп `func (s *Store) ReadAccount(`). ⚠⚠ **ЭРРАТА 06.09 — ШКАЛЫ В ГЛАВАХ БОЛЬШЕ НЕТ.** Лендинг формы заказа похоронил `DefaultPerChapter`, `Scale` и потолок-в-главах целиком: объём заказывается тремя формами (вся книга · до главы N · знаками), холд считается от ПРОЕКЦИИ ЦЕНЫ ДВИЖКА (`k × ожидаемое + шаг [+ книжный бонд]`), а заказ хранится ИДЕНТИФИКАТОРОМ главы, не порядковым номером. Абзац ниже оставлен как история эры шкалы и НЕ описывает сегодняшний провод. Тело — `D39.208`. Прежняя редакция: Шкала — в ГЛАВАХ: `CeilingBounds` — «in CHAPTERS. The conversion to money lives on the platform and is not exposed here in any form» (`docs/architecture/14-api-contract/openapi.yaml`, греп `The conversion to money lives on the platform`); клиенту запрещено клампить повторно; `max_chapters: 0` = состояние «исчерпано» вместо шкалы. ⚠ **Испр. 05.09: запрет отозван (D39.196), но СЕГОДНЯШНИЙ `Usage` действительно без сумм — поле приезжает паком формы заказа, схема впереди кода не едет.** Ниже — что сервится сейчас: `Usage` несёт ТРИ обязательных члена — `state` (ok/low/exhausted), `remaining_percent` и `halt_reason` — и ни окон, ни сумм (`docs/architecture/14-api-contract/openapi.yaml`, греп `required: [state, remaining_percent, halt_reason]`); продуктовый носитель — ПТ-35 (`docs/product-requirements.md:63`=`ПТ-35 | Лимиты «как в Claude Code»`): стоп по потолку = `paused` + оповещение «перевод остановлен: лимиты исчерпаны»; остаток — страница лимитов (S4+), слово владельца В-3. ⚠ **К-13 ЗАКРЫТ** (D39.132 п.2а: `null` на проводе; пере-суждён ревью 28 §4 №9 и НЕ опрокинут — с уходом дневной оси различие «свой потолок / нет кредита» перестало быть продуктовым). ## 5. Открытые дыры и их носители | Дыра | Носитель | |---|---| | ⚠ **Строка 166 — ДВИЖКОВАЯ половина ПОСТРОЕНА 05.09** (`expected_usd` · `book_once_usd` · `step_max_usd` в `manifest --json`, лендинг `81a89e9`, акт D39.206); ⛔ **ПЛАТФОРМЕННАЯ половина ТОЖЕ ПОСТРОЕНА — пере-снято 07.09 ЭТОЙ ЖЕ командой, которой утверждение доказывалось:** `git grep -lE "StepMax|step_max" -- platform/` даёт **19 файлов** (контроль на несуществующем токене — 0), среди них боевые `internal/ingest/manifest.go`, `internal/pgstore/books.go`, миграция `00033_order_and_price.sql`; движковые числа не только читаются, но и СТРОЯТ холд — `platform/internal/pricing/pricing.go:198`=`func (m Model) Hold(expected, stepMax, bookOnce, balance`. ⚠ Прежние две редакции звали дырой сначала обе половины, потом платформенную — обе устарели, и вторая пережила лендинг `81a89e9`. Строка бэклога 166 — кандидат на закрытие при лендинге платформенного пака. Движковая поверхность оценки $/глава; $0.03 — временная мера беты (П-10 зоны) | `docs/PROGRESS.md`, греп `Движковая поверхность оценки` | | ✅ **Строка 78 ЗАКРЫТА 10.09** (акт `D39.232`) — биллящийся decode-fail пишет свою строку и помечен оценкой; остаток другого знака — строка **377** | `docs/architecture/05-decisions-log.md`, греп `## D39.232` | | **Строка 136** — эскроу/intent (см. §3 п.10) | `docs/PROGRESS.md`, греп `Write-ahead intent` | | *(добавка сборщика)* **PD-157 (minor)** — `day_usd` книги флагом не перекрывается, платформой не виден и не задаётся; в `status --json` дневной фигуры нет | `platform/docs/DEFECT_REGISTER.md`, греп `PD-157` | ## 6. Карта носителей темы | Вопрос | Где живёт | |---|---| | Словарь терминов | `docs/glossary.md:58`=`**Деньги платформы:**` | | Управляемый потолок: решение и форма | D39.110 (+поправка D39.112 п.3: п.1 — пересказ, не цитата) | | Максимум шкалы = Balance как есть | D39.115 п.2а; эррата — шапка D-лога, `docs/architecture/05-decisions-log.md:4`=`Balance КАК ЕСТЬ` | | Семантика `--ceiling-usd` | D39.122 п.2в; `backend/cmd/tmctl/invocation.go:142`=`the book USD ceiling in force for THIS RUN ONLY`, `backend/cmd/tmctl/main.go:261`=`--ceiling-usd is NOT a per-run budget` | | Формула аргумента потолка (без reserved) · ставка · settle-формы | D39.123 п.2б/г/д; PD-158/PD-159/PD-144 в реестре | | Код денег платформы | `platform/internal/pgstore/credits.go` · `pgstore/runs.go` · `runs/spawn.go` · `runs/reconcile.go` · `pricing/pricing.go` | | Ратификации, на которых стоит этот док | ⚠ **добавлены 05.09: `D39.203`** (владелец: движку МОЖНО говорить «сколько добавить»; наружу идёт ДО-ВЫЗОВНАЯ ОЦЕНКА, не стоимость) · **`D39.204`** (отказ резервации на ретрае деградирует во ФЛАГ; решение принято и ИСПОЛНЕНО — ⚠ **испр. 16.09: пак «КУПЛЕНО — ЗНАЧИТ ДОСТАВЛЕНО» принят актом `D39.255`, строка 291 ЗАКРЫТА**, код движка тронут (метка `retry_unaffordable`, отдача купленного до пере-делки); ⚠ испр. 15.09: конструкция пере-решена владельцем 11.09 — СТАНДАРТНАЯ ОСТАНОВКА с меткой позиции и недостачей в кадре `ceiling`, а не флаг; «Эррата 15.09-а») · **`D39.206`** (акт приёмки денежного стопа). ⚠ **И новые движковые денежные носители того же лендинга:** `backend/internal/pipeline/reservegate.go` (шлюз резерваций и `shortfallMicroUSD`) · `priceprojection.go` (проекция цены книги) · `internal/runevents/runevents.go` (`Ceiling.ShortfallMicroUSD`, `Finished.Money`, поток 1.3). · **D39.179** (пере-пин цен 16.08, ×4.47; норма «стабилен чистый ФАЙЛ» — п.4) · **D39.181** (закон раскрытия: признак объёмного стопа едет числом) · **D39.182** (пак «деньги и честность»: план банк-ролей режется до первого вызова; порог согласия без контура банк-ролей) | | Код денег движка | `backend/internal/store/ledger.go` · `store/store.go` · `pipeline/stagerun.go` · `pipeline/status.go` · `config/book.go` · `pipeline/rebill.go` (согласие на пере-оплату) · `pipeline/terminologist.go` (книго-широкие бюджеты банк-ролей) · `pipeline/volume.go` (объёмный потолок) | | Дефекты денег | `platform/docs/DEFECT_REGISTER.md` (PD-104·113·144·157·158·159) | | Бэклог-строки | `docs/BACKLOG.md` строки 136 · 137 · **377** · **382** (⚠ 78 ЗАКРЫТА 10.09 актом `D39.232`) (⚠ испр. 05.09: **166 отсюда СНЯТА** — движковая половина оценки построена и опубликована лендингом `81a89e9`; открыта только платформенная) (135 и 165 закрыты D39.131) | | Контракт наружу | `14-api-contract/openapi.yaml` (⚠ **`CeilingBounds` РЕТИРОВАН лендингом формы заказа 06.09** — его место занял `RunOptions` с `chapters_left`/`affordable_chapters`/`verdict` и денежными границами; `Usage`); ПТ-35 | | ⚠ Запрет денег на экране — **ОТОЗВАН 05.09 (D39.196)**. Живым остаётся ПТ-35 в его четырёх пунктах: цены моделей · стоимость стадий · стоимость ВЫЗОВОВ · структура наших расходов. ⚠ И различение `D39.203`, без которого пункт про вызовы читается неверно: наружу идёт ДО-ВЫЗОВНАЯ ОЦЕНКА (резервация перед звонком), а запрет написан про СТОИМОСТЬ — что списано по факту; сеттлмент не выходит | D39.84; D39.100/К-8; ПТ-35; **D39.196**, эррата 05.09-а | --- ## РАЗБОР 04.09: КНИГУ НЕЛЬЗЯ ДОЧИТАТЬ — три независимых чтения; форма заказа РЕШЕНА владельцем 05.09 (`D39.196`) > **⚠ СТАТУС ИЗМЕНИЛСЯ 05.09: ФОРМА ЗАКАЗА РАТИФИЦИРОВАНА — `D39.196`.** Решение владельца: баланс аккаунта — ДЕНЬГАМИ и виден; > ползунок потолка — деньгами; рядом ответ «хватает на полную книгу или нет»; второй ползунок объёмом — ГЛАВЫ когда книга > распозналась, ЗНАКИ когда нет (о нераспознавании говорим ЯВНО); старт = холд с баланса; отдельная кнопка «книга целиком». > Тем же решением ОТОЗВАН запрет «денежных полей в UI нет вовсе». Строки исполнения — 276–283. > **Ниже — РАЗБОР, из которого решение выросло.** Он не контракт: при конфликте с `D39.196` побеждает нота. Носитель дефекта — `PD-440`/`PD-441` в регистре платформы. Собрано по > прямому указанию владельца «посоветуйся с зонами и со старшей моделью»: читали независимо зона движка, > зона платформы и внешний рецензент другой модели; расхождения между ними названы, а не сглажены. **ЧТО НАБЛЮДАЛОСЬ.** Первый живой ПЛАТНЫЙ прогон книги через API (04.09) остановился, не продвинув книгу ни на юнит, и воспроизвёлся дважды. Прогон умирает за 10–15 секунд, списание не двигается. **ПОЧЕМУ — цепь, проверенная тремя чтениями независимо.** Запас нового прогона на НОВЫЕ траты равен ⚠ **испр. 06.09** — не `chaptersLeft × ставка` (ставки нет), а **холд от проекции движка**: `k × ожидаемое(заказ) + шаг`, где шаг — крупнейшая НЕДЕЛИМАЯ резервация (`platform/internal/pricing/pricing.go`, греп `func (m Model) Hold(`). Резерв ОДНОГО редакторского вызова — worst case по `maxTokens` (`backend/internal/pipeline/stagerun.go`, греп `func (r *Runner) callEstimateUSD`), замеренный отказ в ряду `PD-440` — `denied estimate=$0.069828`. Отказ резервации трактуется как **авария всей книги**: `runWave` зовёт `cancel()` производного контекста на ПЕРВОЙ ошибке любого рода, и уже допущенные параллельные вызовы гасятся. **ТРИ УТВЕРЖДЕНИЯ, СОШЕДШИЕСЯ У ВСЕХ ТРЁХ ЧТЕНИЙ:** 1. **Форма потолка неверна, а не только его размер.** Запас сжимается вместе с остатком книги, стоимость неделимого шага — нет. Ставку можно поднять, стену это отодвинет, но не уберёт. Правильная форма — `max(главы × ставка, шаг)`, а не `главы × ставка`. 2. **Резервацию убирать нельзя.** Она — единственный сериализованный контроль и единственное, что держит обещание «не потратить больше, чем дано». Отраслевой стандарт тот же (см. прайор-арт ниже). 3. **Менять надо ТРАКТОВКУ ОТКАЗА**, а не наличие резервации: «не начинаем новое» вместо «гасим всё». ⛔⛔ **ЭРРАТА 05.09 — ВСЁ НИЖЕ ОПИСЫВАЕТ ДВИЖОК ДО ПАКА «ДЕНЕЖНЫЙ СТОП» (приёмочный `81a89e9` + дофикс `616a8e4`, акт приёмки **D39.206**; ⚠ испр.: раньше здесь стояла D39.203 — это нота ВЛАДЕЛЬЦА про «сколько добавить», а не акт, и назван был только дофикс-коммит). Порог «книга короче десяти глав не переводится» СНЯТ ЛЕНДИНГОМ.** Стена стояла на том, что отказ ОДНОГО воркера ронял ВСЮ волну ⇒ резервы всех четырёх обязаны были уместиться в потолок разом. Пак это и чинил (его промт `docs/archive/prompts/BACKEND_MONEYSTOP_SESSION_PROMPT_2026-09-05.md` называл порог целью): отказ ЗАЩЁЛКИВАЕТ волну — новое не стартует, допущенное дорабатывается, — а обязательный вызов ЖДЁТ settle, когда settle может помочь. Пины: `backend/internal/pipeline/moneystop_test.go`, греп `TestUnderACeilingTheWaveFinishesWhatItAdmitted` и `TestWaitingForHeadroomDeliversTheWholeBook` («a ceiling that admits one call AT A TIME must still deliver the book»). ⇒ **Честное состояние на 05.09:** движок больше НЕ требует, чтобы вся волна умещалась в потолок; короткая книга идёт по одному вызову. Остаточный пол — ОДНА резервация (`step_max_usd` ≈ $0.07 ⇒ ~3 главы по ставке $0.03; до ИСПОЛНЕНИЯ `D39.204` удвоение на ретрае поднимает это до ~5 — ⚠ решение владельца принято 05.09; ⚠ **испр. 16.09: КОД ИСПОЛНЕН и принят актом `D39.255`** — строка 291 закрыта, и «до исполнения» ниже читается как ИСТОРИЯ, а не как действующая стена; ⚠ испр. 15.09: по «Эррате 15.09-а» исполнение превращает отказ на ретрае в штатную остановку с меткой позиции и недостачей, а не во флаг с отгрузкой, — поэтому «до исполнения» больше не значит «после исполнения пол ниже»: снимается невидимость оплаченной половины работы, а не сам пол). ⚠ **Новый порог ВЫВЕДЕН, НЕ ЗАМЕРЕН** — отчёт пака называет это честно (`backend/docs/MONEYSTOP_REPORT.md` §9: цена защёлки не замерена, числа проекции с фактом не сверены). Замер стоит одного $0.15-прогона пятиглавой книги и передан полигону. ⚠ **Испр. 10.09: платформенная половина (потолок ДЕНЬГАМИ, D39.196) ПОСТРОЕНА, и прежний отрицательный замер здесь протух.** Та же команда сегодня: `git grep -lE "StepMax|step_max" -- platform/` → **20 файлов** (контроль: несуществующий токен — 0; файлов под `platform/` в индексе — 339), среди них боевые `platform/internal/pricing/pricing.go` (греп `func (m Model) Hold`), `platform/internal/ingest/manifest.go`, `platform/internal/pgstore/books.go`. Холд считается ИМЕННО на этих числах. ⚠ Строка **63** этого же файла предупредила об этом ещё 07.09 («вторая пережила лендинг `81a89e9`»), а фраза стояла — то есть предупреждение в доке само по себе не чинит дока. ⚠ **Класс ошибки, ради которого эррата написана так подробно: оркестратор заленджил пак, чей промт заказывал снятие этой стены, принял его — и в тот же час предупредил владельца о стене как о действующей, читая ЭТОТ абзац.** Канон, не обновлённый лендингом, врёт громче, чем отсутствующий. **ЧИСЛА, В КОТОРЫХ ЧТЕНИЯ РАСХОДИЛИСЬ (ДО-ПАКОВАЯ АРХЕОЛОГИЯ) — и оба верны на своём числе воркеров.** Зона платформы: стена стоит везде, где `chaptersLeft × ставка < workers × шаг`, при боевых четырёх воркерах это `chaptersLeft ≤ 9` — недостижим не хвост, а **последние девять глав любой книги**. Внешний рецензент: даже при ОДНОМ воркере `2 × $0.03 = $0.06 < $0.07` одного вызова, то есть **последние две главы недостижимы в принципе**, независимо от параллельности. Второе — инвариантный пол, первое — сегодняшняя конфигурация. ⚠ **ПРЕДСКАЗАНИЕ ПОДТВЕРЖДЕНО ЗАМЕРОМ 04.09 — это больше не предсказание, а ПОРОГ.** Замер за $0 (книга `bk_P5UCXKHDO4HFWSH3` заведена настоящим интейком, баланс не двинулся): `run-options` отдаёт `max_chapters: 5`, то есть максимум покупки пятиглавой книге — **$0.15 при стоимости волны $0.277633**. Наблюдённая волна снята из отказа самого движка (`book USD ceiling reached ($0.548319) (committed=$0.278319 reserved=$0.207805, denied estimate=$0.069828, ch5/chunk0/edit)`): три реальных резерва $0.207805 + отказанный $0.069828. ⚠ Произведение `4 × $0.069828 = $0.279312` — ВЕРХНЯЯ оценка, а не наблюдение: вызовы волны не равны (отношение 2.976, промпты чанков разной длины), и счёт «три» ВЫВЕДЕН из `waves.workers: 4` минус отказанный, движок напечатал сумму, а не количество. **Порог одинаков по обоим числам: 9.25 и 9.31 ⇒ книга проходит только с ДЕСЯТИ непереведённых глав.** Последнее возражение («у короткой книги куски мельче») снято замером: разрезка совпала побайтно (`2·1·1·2·1` против первых пяти глав длинной), то есть короткая книга содержит ровно тот чанк, чей резерв движок и назвал. ⇒ **Сегодня сервис не может перевести книгу короче десяти глав — не «дорого», а невозможно при любых деньгах на счету.** ⚠ Сама пятиглавая книга НЕ запускалась: заказ был на $0 и исполнен за $0; живой прогон добавил бы лишь наблюдение, на какой волне она встанет. **ЧЕТВЁРТОЕ, ЧЕГО НЕ БЫЛО В ИСХОДНОМ ДИАГНОЗЕ (зона движка).** Резерв завышен ПО ПОСТРОЕНИЮ: оценка считается по потолку выдачи. Под нынешним поведением он никогда не превращается в факт — его отпускают, и следующее решение о допуске принимается против тех же завышенных чисел. Под «не начинаем новое» соседи доседают по ФАКТИЧЕСКОЙ цене и освобождают разницу. Это само по себе может быть разницей между «встала» и «дошла». **ПЯТОЕ — ПРЕЦЕДЕНТ ВНУТРИ ЭТОГО ЖЕ ПАКЕТА, и он решает спор о направлении.** Объёмный потолок уже построен с нужной семантикой и САМ противопоставляет себя денежному: `backend/internal/pipeline/volume.go`, греп `THE STOP IS A COMPLETION, NOT A HALT` — «денежный потолок останавливает прогон ПОСРЕДИ работы, которую он собирался сделать». Там же названа причина, по которой допуск не сделан счётчиком у воркеров: «воркер, которому отказали в слоте, был бы вынужден бросить свой элемент навсегда» — то есть ровно тот дефект, который денежный путь и имеет. Плюс три места уже деградируют по `errReserveCeiling` вместо аборта (`escalation.go`, `repair.go`, `terminologist.go`); не деградирует только главная волна. **ШЕСТОЕ — ДЕФЕКТ, ПЕРЕЖИВАЮЩИЙ ЛЮБУЮ ПОЧИНКУ РАЗМЕРА И ФОРМЫ (зона платформы).** Стена МОЛЧАЛИВА: прогон, вставший мгновенно, неотличим на всех поверхностях от работавшего — одинаковый статус, одинаковая причина паузы, одинаковая полоса (наблюдение H16 зонного журнала, два прогона: $0.081 и $0). Недобрать можно при любом потолке; сказать об этом сегодня нечем. **ВЕС — ЖИВУЧЕСТЬ, НЕ ДЕНЬГИ.** Мёртвые прогоны рассчитались в ноль, леджер сходится тремя источниками до микродоллара. Пользователь теряет ВРЕМЯ. ⚠ Отдельно и с другим весом — `PD-441`: на пути отмены есть расход, который, ЕСЛИ он был, невидим для леджера по построению. Доказуемая форма именно такая; «деньги потрачены» НЕ измерено. Одна движковая попытка — до трёх запросов к провайдеру (`backend/internal/llm/httpllm.go`, греп `for att := 0; att < profile.MaxAttempts`, дефолт 3) под ОДНИМ резервом и ОДНОЙ строкой `request_log`: след не просто беден, он агрегирован и не различает «не ушло ничего» от «ушло трижды». **ПРАЙОР-АРТ (внешний рецензент, ссылки — в отчёте сессии).** LiteLLM делает буквально наше: worst-case-резерв → отказ до отправки → замена фактической ценой; их известные баги — про ОБХОД бюджета при параллельных запросах, то есть отраслевая беда противоположна нашей. Ближайший по смыслу стандарт — Diameter Credit-Control (RFC 4006/8506) с `Final-Unit-Indication`: «это последний грант, доработай его и не начинай нового» — ровно та семантика, которой нам не хватает. Kubernetes `ResourceQuota`: отказ — это недопуск НОВОГО, уже созданное квота не трогает. Slurm `GraceTime`: слив вместо убийства. Карточная пре-авторизация: холд с запасом, списание по факту. ⚠ Про биллинг при обрыве: OpenRouter документирует, что для непотоковых запросов отмена клиентом биллинг НЕ останавливает — это факт о ЕГО платформе, для наших провайдеров вопрос обязан быть закрыт вендор-докой по правилу `experiments/00-provider-quirks.md`, а не рассуждением. **⚠ РАЗВИЛКА, КОТОРУЮ ОРКЕСТРАТОР НАЗВАЛ РАЗВИЛКОЙ, ЕЮ НЕ ЯВЛЯЕТСЯ — снято словом владельца 04.09.** Формулировалось так: «потолок = баланс ⇒ холд берёт весь баланс ⇒ вторая книга не стартует, пока не рассчитается первая», и выносилось владельцу на решение. Владелец: это не решение, а то, как деньги работают — выделил книге сумму, она занята, на занятые вторую не стартуешь; управляется тем, сколько выделил. **Механика, поданная как вопрос к владельцу, — дефект оркестратора, а не открытый пункт.** **ЧТО ПОД НЕЙ ЛЕЖАЛО НА САМОМ ДЕЛЕ И ОКАЗАЛОСЬ ХУЖЕ: выделить деньги СЕГОДНЯ НЕЛЬЗЯ.** Шкала прогона — в ГЛАВАХ (⚠ **ретировано 06.09, см. эрратy выше** — историческая запись эры шкалы), и канон говорил это прямо: `14-api-contract/openapi.yaml`, схема `CeilingBounds`, греп `Bounds of the run-limit scale, in CHAPTERS` — «перевод в деньги живёт на платформе и наружу не отдаётся ни в каком виде». Человек двигает главы; сумму он не видит и выбрать не может. Ограничение при этом ДЕНЕЖНОЕ, перевод между единицами делает сломанная ставка — отсюда и наблюдаемая нелепость: пятиглавой книге шкала честно предлагает `max_chapters: 5`, а купить это не может ничего. ⇒ **Единица выбора и единица ограничения не совпадают, и это корень, а не следствие.** Направление, объявленное владельцу 04.09: человек выделяет книге ДЕНЬГИ (по умолчанию — на всю книгу, ограничено балансом), рядом показывается оценка «хватит примерно на N глав» именно как оценка, прогон тратит выделенное и ГОВОРИТ, когда кончилось, а «после этого на другие книги останется ноль» показывается в момент выделения. Порог десяти глав при этом исчезает по построению: считать «остаток глав × ставку» становится негде. Цена — контрактный минор в месте, где сегодня записано «деньги наружу не отдаются», и пере-рисовка ручки фронтом (зона заморожена, первое касание — синк с каноном, момент удобен). **ЧТО СЧИТАЕТСЯ ЗАКРЫТЫМ И БОЛЬШЕ НЕ ОБСУЖДАЕТСЯ:** уже проданные прогоны смену ставки переживают без миграции — продолжение оценивается по тому, за что прогон был ПРОДАН, бюджет читается обратно из холда первой попытки, пин `TestARestartHoldsWhatTheRunWasSoldForWhenTheRateHasMovedSince`. **СЕДЬМОЕ — ЕДИНИЦА УЧЁТА НЕ СОВПАДАЕТ С ЕДИНИЦЕЙ БИЛЛИНГА, и это бьёт не в отчётность, а в ЦЕЛОСТНОСТЬ потолка** (найдено зоной движка чтением под вопрос, проверено оркестратором по строкам). Класс `BilledDecodeError` заведён ровно для того, чтобы оплаченный-но-нечитаемый вызов стал ВИДЕН: рантайм консервативно доседает его по оценке. Комментарий рядом сам называет цену ретрая — `backend/internal/llm/httpllm.go`, греп `each retry is a new billed`, — и по этой причине УСЕЧЁННЫЙ ответ сделан терминальным. Но битое тело осталось ретраябельным, а `retryLoop` при успешном ретрае возвращает ответ и предыдущую ошибку ВЫБРАСЫВАЕТ (греп `if err == nil {` в `retryLoop`). Тогда рантайм видит один успешный ответ, доседает его `Usage`, и оплаченный 2xx перед ним не попадает НИКУДА — ни в списание, ни отдельной строкой лога. **Механизм, построенный чтобы сделать оплаченный вызов видимым, обходится ровно на том пути, где всё прошло хорошо.** Следствие для потолка: допуск выдан один раз под одну оценку; если под ней прошло два оплаченных ответа, `Reserve` об этом не узнает никогда — он сравнивает с потолком сумму, в которой невидимого запроса нет. Потолок не «пробивается громко», он считает не то. ⚠ **Границы:** структурность доказана чтением; ЧАСТОТА пути НЕ измерена и, вероятно, мала — владельцу эти две вещи давать раздельно. Более слабый родственник — сетевой обрыв/таймаут (там же комментарий про `mid-generation RST that re-bills the call`): вход тот же, но «оборвалось на середине ⇒ оплачено» уже вывод о провайдере, а не утверждение кода. **ВЕНДОРСКИЙ ВОПРОС — ДВОЙНОЙ, и вторая половина конкретнее первой:** (1) платит ли провайдер за запрос, оборванный КЛИЕНТОМ после начала генерации; (2) сколько раз он берёт деньги, когда мы ретраим ПОСЛЕ оплаченного 2xx. Второй случай не гипотетический — он уже назван оплаченным в нашем собственном коде. Оба закрываются вендор-докой по правилу `experiments/00-provider-quirks.md`, а не рассуждением. **ГРАНИЦА ИНСТРУМЕНТА, ЗАМЕРЕННАЯ ЭТИМ ЖЕ ЗАХОДОМ.** Каталог мутаций (141 запись, 136 красных, ноль переживших) доказывает ПОВЕДЕНИЕ и молчит о том, что код УТВЕРЖДАЕТ о себе. Круг 3 нашёл четыре сверх-утверждения — и **ни одно не нашла посадка**; обе сегодняшние денежные находки получены чтением под вопрос. Рабочая форма вопроса, выведенная зоной движка: не «правда ли то, что здесь написано», а **«что должно было бы сломаться, если это правда, и ломается ли оно»**.