# Эксперимент 24. Холодный прогон «от двери до ФАЙЛА» — настоящая книга, настоящие деньги > **Пре-регистрация.** Всё, что ниже строки «ГРАНИЦА ФРИЗА», написано ДО первого платного вызова и > закоммичено фриз-коммитом. Результаты дописываются ПОСЛЕ прогона, отдельной секцией, и ничего в > пре-реге не правят: расхождение называется, а не подгоняется. **Зона:** полигон · **Пак:** `docs/POLYGON_COLD_RUN_A_SESSION_PROMPT.md` (редакция 2) · **Дата:** 11.09.2026 **Предмет:** строка бэклога **16** — цена и проходимость книги, ДОВЕДЁННОЙ ДО КОНЦА, не измерены ни разу. ## 0. Что именно проверяется Одна настоящая книга (蛊真人, главы 1–3, zh→ru) проходит весь путь: интейк → цена → холд → прогон → остановка на подпись банка → решение человека через дверь правок → резюм → редакторская волна → сборка выгрузки → скачанный файл → **человек читает первый абзац**. У каждого узла напечатано, ЧЕМ он предъявлен. ⚠ **Судьи на этом пути НЕТ.** Боевой `pipeline-c1.yaml` несёт две стадии — `draft` и `edit`; `role: judge` живёт только в `c2`. `escalation.budget_usd: 0` ⇒ эскалация не исполняется; `gates.coverage.enabled: false` ⇒ из пяти гейтов работают четыре. Проверяемый путь: **черновик → редактор → четыре $0-гейта → выгрузка.** ⚠ **О качестве перевода пак не судит вовсе:** 蛊真人 узнаётся моделями (претрейн), и любое суждение о качестве было бы суждением о памяти модели. Чтение человеком отвечает на один вопрос — «это связный русский текст, а не мусор и не обрывок». ## 1. ГРАНИЦА ФРИЗА — что зафиксировано до первого цента | Предмет | Значение | |---|---| | Фриз-sha | `b0f5d8915c8553993f42e6e3a9d6fbd18adfa68e` | | Фриз-дерево | локальный **клон** `~/tm-coldrun-a/freeze` (0 грязных строк при 13 в главном дереве) | | Бинари | `tmctl` · `tmplatformd` · `tmplatformctl`, собраны из клона, у каждого `vcs.revision=b0f5d89…`, `vcs.modified=false`, строк `vcs.*` — 3 | | Исходник (оригинал) | `books/gu-zhenren/coldrun-v16/guzhenren-ch1-10.gb18030.txt`, 57838 байт, sha256 `0b5f9b0266d8c32ac717a41211d2ed15d4f2a07f37a701fb686e255ab6c89f37` | | Исходник (СРЕЗ, что покупается) | главы 1–3, 17564 байта, sha256 `ed870ba6065ce3eb4eec12e80238efbc2be8511f38334ab1d92d5693f4ef4df9` | | Фактический pipeline-YAML | `~/tm-coldrun-a/mirror/cfg/pipeline-c1.yaml`, sha256 `a46b33ee65b7713b4fbf86f64c788ba6b11eb0a219170c145a6205088156ad49` — **побайтно равен** `backend/configs/pipeline-c1.yaml` фриза (`cmp`) | | Артефакт контраста | `mining-contrast.zh.txt` = jieba 0.42.1 `dict.txt`, sha256 `7197c3211ddd98962b036cdf40324d1ea2bfaa12bd028e68faa70111a88e12a8`, 349 046 строк | | Шаблон книги | `~/tm-coldrun-a/book-template-c1.yaml`, sha256 `f66e681ff55b2653fc87944f45d7214bac0cd209f42102f642267d1ef61d8057` | | Потолки книги (шаблон) | `book_usd: 1.25` · `day_usd: 2.50` — машинный бэкстоп; сессия останавливается и пингует РАНЬШЕ, на факте $1.00 | | Грант платной учётки | $2.50 (отдельная учётка, не дымовая — иначе леджер смешивает дымовые холды с боевыми) | **Объём заказа — вариант (а) пака:** режется ИСХОДНИК, грузится срез трёх глав. Довод сильнее, чем «сверка честнее»: при варианте (б) семь незаказанных глав — это ДЫРЫ, дверь обязана отдать файл `--partial` с пометкой (`D39.178`), и центральный критерий `exports.complete = true` недостижим ПО ПОСТРОЕНИЮ. **Срез по $0-манифесту (`tmctl manifest --json`, читающий верб, ключей не требует):** | Глава | `units_total` | `chunks_total` | `expected_usd` | символов | |---|---|---|---|---| | 1 `第一节:纵身亡魔心仍不悔` | 2 | 2 | 0.032424 | 3287 | | 2 `第二节:逆光阴五百年觉悟` | 1 | 2 | 0.028459 | 3051 | | 3 `第三节:请一边玩蛋去` | 1 | 2 | 0.023237 | 2421 | | **книга** | **4** | **6** | `expected_usd 2.08412008` · `book_once_usd 2.00` · `step_max_usd 0.13647876` | 8759 | ⚠ `expected_usd` книги включает `book_once_usd` $2.00 — это ПОТОЛКИ контура банка (`gates.terminology.budget_usd: 1` + `classify_budget_usd: 1`), а не смета. Смета текста — сумма глав, **$0.084120**. Пинится **сумма `units_total` по заказанным главам = 4** (пин «≥1 глава с `units_total ≥ 2`» из первой редакции пака охранял то, чего не бывает). ## 2. Пре-рег-таблица: узел → что ДОЛЖНО произойти → чем предъявлено → что считается провалом | # | Узел | Ожидание (числом или строкой) | Чем предъявлено | Провал узла | |---|---|---|---|---| | 1 | Стенд — мой | слушатель на `127.0.0.1:8097` принадлежит МОЕМУ pid; на `11434` — заглушки нет вовсе (платная фаза) | `ss -ltnp` с pid | на порту чужой процесс | | 2 | Код — из фриза | у трёх бинарей `vcs.revision=b0f5d89…`, `modified=false`, строк `vcs.*` > 0 | `go version -m` | любое из трёх не так ⇒ СТОП | | 3 | Гость и деньги | учётка заведена `POST /auth/dev-login`; грант $2.50 одной строкой `credit_ledger.kind='grant'` | `tmplatformctl balance` + строка леджера | самореги нет, пополнения по HTTP нет — это ожидаемо | | 4 | Интейк | `201`, `chapter_count = 3`, `character_count` ≈ 8759 | тело ответа + строка `books` | не 3 главы ⇒ СТОП до оплаты | | 5 | Срез совпал | `units_total = 4`, по главам 2/1/1 | `chapters`+`units` в Postgres против таблицы §1 | расхождение ⇒ СТОП до оплаты | | 6 | Цена и бонд | `term_consistency_funded: true`, `verdict: covers_all`, `affordable_chapters: 3`, `hold ≈ 2 241 629 µUSD` (= ⌈expected×1.25⌉ + step_max + бонд 2 000 000) | `GET /v0/books/{id}/run-options` | `false` ⇒ СТОП до оплаты (тихая потеря консолидации терминов) | | 7 | Старт | `202`, `status: translating`, `ordered_chapters: 3`; строка `runs` с `bond_funded = t` | ответ + строка `runs` | отказ старта ⇒ СТОП и пинг | | 8 | Ключи живы | первый платный вызов не отказан по авторизации | `request_log.err` пусто на первой строке | отказ провайдера ⇒ СТОП и пинг (не моя поломка) | | 9 | Кадры | `events.jsonl` несёт `hello · progress · unit_done · spend · bank_stop · finished`; `run_attempts.last_seq` растёт | счёт кадров по типам + `last_seq` | кадров нет при живом юните ⇒ дефект | | 10 | Банк-стоп | `runs.status = awaiting_bank`, exit **3**, `.bank.json` несёт непустой `proposed[]` | статус + exit + копия сайдкара | стоп не наступил ⇒ **исход «узел не наступил»**, а не провал (三 условия разом: `--verify-bank` И edit-волна И некогда не показанный кластер) | | 11 | Проекция банка на платформе | `bank_terms` = **0** ПОКА прогон не закрыт и **> 0** ПОСЛЕ | `GET /v0/books/{id}/bank` + `select count(*)` | ноль ПОСЛЕ закрытия ⇒ дефект | | 12 | Дверь правок | `POST …/bank/corrections` `preview:true` → `changed:true`; затем `preview:false` → `accepted[0].state = applied`; на диске появляется `.mined-delta.yaml` | тело ответа + файл | отказ двери ⇒ дефект | | 13 | Резюм | `202`, вторая строка в `run_attempts` (attempt_no = 2), `exit_code = 0` | `run_attempts` | прогон не возобновился ⇒ дефект | | 14 | Книга переведена | `units.state = translated` для всех **4** юнитов | `select state, count(*)` с контрольным счётом юнитов книги | любой юнит не `translated` ⇒ книга не доведена | | 15 | Сборка | `tmctl build`: `complete: true`, `pending/withheld/incomplete/stale/ghost = 0`, `stale_unknown: false`, `config_drift: false` | stdout сборки (на API этих полей НЕТ) | любое не так ⇒ книга не целая | | 16 | Выгрузка | `exports.state = ready`, `exports.complete = t` в Postgres, `size_bytes` = числу скачанных байт | строка `exports` + `%{size_download}` | `complete` не `t` ⇒ дверь отдала книгу с дырой | | 17 | ФАЙЛ | скачаны байты, sha256 напечатан; EPUB читается структурно (zip + `container.xml` + `nav` + spine = 3) | sha256 + разбор zip | файл не открывается ⇒ провал | | 18 | Человек прочитал | первый абзац перевода — связный русский текст | цитата в отчёте | не текст ⇒ провал | | 19 | Деньги сошлись | Σ `request_log.cost_usd` ≈ `spend.committed` ≈ Σ `credit_ledger` settlement ≈ (грант − баланс); открытых резерваций **0** | четыре счёта в µUSD рядом | расхождение — **назвать**, не подгонять | | 20 | Ничего не реплеено | на платной базе `count(*) where tm_hit=1` печатается С РАЗБИВКОЙ ПО ПОПЫТКАМ, `distinct model_actual` не содержит `local-*` | запросы к `project.db` | есть `local-*` ⇒ прогон измерил заглушку | | 21 | Расхождение форм | механический счёт по ОТГРУЖЕННОМУ тексту: сколько канонических терминов отданы более чем одной передачей и в каких главах | `eval/door_to_file/spread.py` + `tmctl export --json --pairs` | — (это ЗАМЕР, у него нет «провала») | ⚠ **Про «три следа»: их не три.** Postgres платформы МАТЕРИАЛИЗУЕТСЯ из `events.jsonl` движка (`Sink materializes a stream into the reporting database`), а текст, манифест и файл платформа берёт теми же вербами `tmctl`. ⇒ согласие Postgres, диска движка и отчёта сборки — это **одна база, согласная сама с собой**, а не три свидетеля. По-настоящему независимы только **скачанные байты и их sha256**; биллинга провайдера у нас нет. Это не отменяет сверку — это меняет то, что она доказывает. ## 3. Стоп-правила (остановка и пинг оркестратору, не решение сессии) 1. фактическая трата дошла до **$1.00** (1 000 000 µUSD); 2. `runs.paused_reason` НЕПУСТО — любое из четырёх (`run_limit_reached · credit_exhausted · daily_ceiling · ceiling_unknown`); 3. срез не совпал с §1 — стоп ДО оплаты; 4. `term_consistency_funded: false` — стоп ДО оплаты; 5. бинарь не из фриза — стоп до устранения. ## 4. Инструменты (зафиксированы фризом вместе с пре-регом) * `eval/door_to_file/drive.sh` — драйвер фаз с гейтами; отказывает, а не предупреждает. * `eval/door_to_file/collect.py` — сборщик следов: фриз-гейт, Postgres, диск движка, отчёт сборки, деньги. * `eval/door_to_file/spread.py` — счёт расхождения форм по отгруженному тексту (строка бэклога 406). * `eval/door_to_file/stub/` — $0-заглушка провайдера `local` (перенос штатной `fakeProvider` стенда). * `eval/door_to_file/zeropipe/` — рендер $0-пайплайна (перенос штатного `zeroCostPipeline`). **Позитивный контроль каждого прибора исполнен ДО платной фазы** (исход по пунктам — §18): подсаженный ненулевой банк, удержанный юнит и открытая резервация красят сборщик; книга с дырой красит отчёт сборки; чужой и беcштамповый бинарь красят гейт; самотест `spread.py` ловит подсаженную вторую форму. --- # РЕЗУЛЬТАТЫ (дописано ПОСЛЕ прогона; ничего выше не правлено) ⭐ **ГЛАВНОЕ ПОМИМО ЦЕНЫ: впервые в проекте консистентность терминов замерена ПО ТЕКСТУ, КОТОРЫЙ ПОЛУЧИЛ ЧИТАТЕЛЬ, а не по черновику и не по фикстуре — и ответ вышел не «всё хорошо», а СЧЁТ.** Движок сам называет термины, у которых форма банка и форма черновика могли разойтись, и называет их ДВУМЯ пометками в `bank-stop.txt`: `spread=N` (черновик дал N разных форм) — таких **9**, и `INVENTED(no draft proposed it)` (форму банка не предложил НИ ОДИН черновик) — таких **11**, из них **7** вне первой девятки. **Итого популяция вопроса — 16 терминов из 69.** Что с каждым стало в отгруженном файле, проверено поимённо (§7.1 и §7.2): * **9 из 16** отданы формой БАНКА (`古月山寨` — 5 из 5 при трёх черновых формах, `甲等资质` — 6 при трёх, `三转蛊师`, `魔道巨擘`, `丙等`, `乙等资质`, `江城子`, `邪魔`, `话事人`); * **6 из 16** отданы одной формой, но НЕ банковской (`高脚吊楼`: банк «дом на сваях» — в файле «свайный дом» ×4, главы 1 и 3; `凤雏`: банк «Молодой Феникс» — в файле «Юный Феникс», глава 2; `老嬷嬷`: банк «старая мамка» — в файле «нянька» ×2, глава 3; плюс три коротких формы); * **1 из 16** отдан ДВУМЯ формами («церемония открытия апертуры» ×9 в главах 1–3 и «великая церемония открытия апертуры» ×1 в главе 1 — обе формы сосуществуют в одной главе). ⇒ **Механизм РАБОТАЕТ и работает не везде, и теперь у этого есть числа.** Счёт форм, не суждение о качестве. Разбор — §7.1 и §16.8; цена контура — §5.3. **Книга дошла до конца.** 蛊真人, главы 1–3, zh→ru, прогон `run_FTHA7S6SUCJIXS2A` над книгой `bk_ROEHZBD46ALFI43E`: старт 04:29:19, финиш 04:54:50 (**25 мин 31 с**), две попытки (банк-стоп и резюм), `status = ready`, `paused_reason` пусто, `failure_reason` пусто. Все **4** юнита `translated`, `exports.complete = true`, файл скачан и прочитан. ## 5. Что стоила доведённая до конца книга **$0.419424** — и по платформе, и по движку. ⛔ **ПЕРВАЯ РЕДАКЦИЯ ЭТОГО АБЗАЦА БЫЛА НЕВЕРНА, снимаю её:** я написала «419 424 по платформе против 419 423 по движку, разница — округление при конверсии». Расхождения нет вовсе. **419 423 — это МОЙ floor от float-суммы `request_log`, и такого числа нет ни в одном артефакте движка.** Движок объявляет свою цифру сам, кадром `spend`: последний кадр `events.jsonl` несёт `{"committed_micro_usd": 419424}`, и он ceil-ит КАЖДОЕ накопительное значение (первый кадр: 12045 при 12044.12). Поймал критик чисел; я приписала платформе округление, которое делает движок, и выдумала несуществующее расхождение между ними. **Семь путей счёта, и все семь сошлись:** | Путь | µUSD | |---|---| | Σ `request_log.cost_usd` (движок, телеметрия; дробное) | 419 423.288 | | `spend.committed_usd` (движок; дробное) | 419 423.288 | | Σ `checkpoints.cost_usd` (движок, 33 чекпойнта; дробное) | 419 423.288 | | **кадр `spend` движка, последний** (`events.jsonl`) — целое, движок ceil-ит | **419 424** | | Σ `credit_ledger.settlement` платного аккаунта | **419 424** | | грант − баланс того же аккаунта | **419 424** | | `run_attempts.spend_micro_usd` ПОСЛЕДНЕЙ попытки | **419 424** | ⚠ Три дробных пути — это ОДНО число 419 423.288, не три разных; четыре целых — то же число после ceil, который делает движок на каждом кадре. Спора между ними нет и не было. ⚠ **И сразу честно про эти «семь путей»: независимый среди них ОДИН.** `request_log`, `checkpoints` и `spend` — три записи одного и того же вычисления движка; `settlement` и баланс — два чтения одного леджера; а сам леджер взял число У ДВИЖКА (через `run_attempts.spend_micro_usd` ← кадр `spend`). Сходимость доказывает, что никто по дороге ничего не потерял и не удвоил, — и ровно это, не больше. ⛔ **Ловушка, на которую я наступила и называю: `spend_micro_usd` попытки — величина НАКОПЛЕННАЯ, а не приращение.** Сумма по попыткам даёт 579 635 µUSD — на 38 % больше правды, потому что вторая попытка несёт в себе первую (`spend_baseline_micro_usd` второй попытки = 160 211 = вся трата первой). Кто просуммирует эту колонку по попыткам, получит завышенную цену книги и не заметит. **Открытых резерваций на конце — 0** (контроль: резерваций этого аккаунта любого состояния — 2). ### 5.0. Что сообщил САМ провайдер — токены, а не доллары Доллары в этом отчёте посчитаны нами (§16.1). Провайдер сообщил вот это, и только это: | модель | вызовов | вход | из них из кэша | выход | |---|---|---|---|---| | `deepseek-flash` | 22 | 71 244 | 22 784 | 126 751 | | `deepseek-v4-pro` | 5 | 29 175 | 9 088 | 51 403 | | **итого** | **27** | **100 419** | **31 872** | **178 154** | **41 488 выходных токенов (23.3 % всего выхода) куплены четырьмя ответами, которые были выброшены.** ⚠ Колонка `reasoning_tokens` равна нулю на **всех 33 строках**. Что это значит — вендор её не отдаёт, или наш адаптер её не разбирает — по этим данным НЕ РАЗЛИЧИТЬ, и я не гадаю. Практическое следствие одно и оно твёрдое: **«сколько из выхода было размышлением» по нашей телеметрии не видно ни на одном вызове**; видно только, что выход упирался в потолок. Для мины §5.2 это существенно — причину «размышление съело потолок» мы читаем из журнала движка (`reason=empty`), а не из счётчиков. ### 5.1. Куда ушли деньги | Стадия и роль | вызовов | $ | |---|---|---| | `draft` / translator (`deepseek-v4-flash`) | 15 | 0.123656 | | `edit` / editor (`deepseek-v4-pro`) | 5 | 0.230471 | | `terminology` / classifier | 6 | 0.035803 | | `terminology` / terminologist | 7 | 0.029494 | | **итого** | **33 строки = 27 ВЫЗОВОВ + 6 подстановок из чекпойнтов** | **0.419423** | ⚠ Сумма четырёх округлённых подытогов даёт 0.419424 — на 1 µUSD больше настоящего итога 0.419423. Это округление ЧЕТЫРЁХ строк до шестого знака, а не потерянный вызов: при полной точности подытоги 0.123656064 + 0.230470592 + 0.035802968 + 0.029493664 = 0.419423288. Названо, чтобы читатель не искал пропавший цент. ### 5.2. ⛔ ЧЕТВЕРТЬ ДЕНЕГ КУПИЛА ПУСТОТУ **4 вызова из 27 вернулись с `finish_reason = length` и `ok = 0`, и стоили $0.106472 — 25.4 % всей траты книги.** (Строк в `request_log` 33, но шесть из них — бесплатные подстановки из чекпойнтов, а не вызовы: «вызовов 27» и «строк 33» — разные величины, и первая редакция отчёта их смешивала.) Это ровно задокументированная мина `00-provider-quirks.md` §катовер п.1–2: размышление съедает `max_tokens`, `content` приходит пустым. Поимённо: | id | стадия / место | модель | `max_tokens` на входе | флаг движка | $ пустого вызова | перевыпуск | $ перевыпуска | |---|---|---|---|---|---|---|---| | 1 | draft ch1/chunk0 | `deepseek-v4-flash` | **8496** | `empty` | 0.012044 | **16992** | 0.021068 | | 5 | draft ch2/chunk1 | `deepseek-v4-flash` | **8496** | `length` | 0.011637 | **16992** | 0.017260 | | 7 | draft ch3/chunk0 | `deepseek-v4-flash` | **8496** | `empty` | 0.011782 | **16992** | 0.018656 | | 31 | **edit ch2/chunk0** | **`deepseek-v4-pro`** | **16000** | `empty` | **0.071009** | **32000** | 0.046861 | ⚠ **Флага ДВА, и они разные:** `empty` — ответ пуст (размышление съело весь потолок), `length` — текст оборван на потолке. В `request_log` оба приходят как `finish_reason = length`, то есть по одной телеметрии их не различить — различает их журнал движка (`disposition=flagged reason=…`). ⚠ **На edit пустой вызов дороже своего же удачного повтора** ($0.071009 против $0.046861 при вдвое большем потолке): неудачный сжёг весь бюджет 16000 токенов на размышление и молчал 249 секунд. ⚠ **Самый дорогой одиночный вызов прогона не дал ничего.** $0.071009 — это 17 % бюджета книги, и это ровно тот класс, про который `pipeline-c1.yaml:81` пишет в комментарии: «thinking ate max_tokens: content="", finish=length». Конфиг ставит на draft `reasoning: "low"` именно против этой мины — **мина сработала ВСЁ РАВНО, и на draft (3 раза), и на edit, где ручка не выставлена вовсе.** ⭐ **Но лечение в движке ЕСТЬ и оно работает:** `stage flagged, regenerating with a larger budget … reason=empty next_max_tokens=16992` — движок опознаёт пустой ответ как флаг `empty` и перевыпускает вызов с удвоенным потолком. Все четыре раза второй вызов вернул текст. То есть путь не рвётся — он дорожает: **платим дважды и выкидываем первый платёж.** ### 5.3. Терминологический контур оплачен ДВАЖДЫ Роли банка исполнились до банк-стопа (строки 10–16, $0.036554) **и снова после резюма** (строки 23–28, $0.028742). Итого $0.065297 — **15.6 % книги** ушло на контур банка, и **$0.028742 (6.9 %) — это второй заход того же контура на той же книге**. Заново ли он нужен после движения банка — вопрос к движку; здесь он просто НАЗВАН и посчитан, потому что раньше эту цену никто не отделял. ### 5.4. Смета против факта — второй замер множителя | Оценка | $ | множитель к факту | |---|---|---| | `expected_usd` движка (нижняя граница по построению: без ретраев и ремонта) | 0.084122 | **×4.99** | | Ожидание пака, ИСПР. 16 дословно | **0.35–0.50**, хвост до 0.65 | факт **внутри вилки** | | Вилка оркестратора по историческому множителю ×3.6 от прогона 08.09 (релей 11.09) | 0.30–0.49 | факт **внутри вилки** | | **ФАКТ** | **0.419424** | — | ⚠ **У «ожидания» три написания, и они различаются на 2 µUSD — назову, чтобы это не читалось как спор:** сумма глав манифеста **84 120** · строка `books` (`expected_micro_usd − book_once_micro_usd`) **84 121** · ответ `run-options` **84 122**. Множитель посчитан от 84 122 (то, что видит покупатель); от 84 120 он был бы ×4.9860 вместо ×4.9859 — на вывод это не влияет, а на доверие к числам влияет. ⇒ Множитель «смета движка → факт» на этой книге **×4.99** при ×3.6 на опорном прогоне 08.09. Это ВТОРАЯ точка, а не подтверждение: две точки дают разброс, а не закон. Причина расхождения названа выше — четверть траты ушла на пустые ответы, которых смета не знает по построению. ## 6. Исход каждой строки пре-рег-таблицы | # | Узел | Ожидание | ФАКТ | Сошлось | Чем предъявлено | |---|---|---|---|---|---| | 1 | стенд мой | pid на порту | `tmplatformd` pid 1049523 на 8097; на 11434 не слушает НИКТО (контроль: 7 слушателей на хосте) | ✅ | `ss -ltnp` | | 2 | код из фриза | три условия | `vcs.revision=b0f5d89…`, `modified=false`, строк `vcs.*` = 3 у всех трёх бинарей | ✅ | `go version -m` | | 3 | гость и деньги | грант одной строкой | `u_ZWUVTETPZL2D6LML`, грант 2 500 000 µUSD, строка `kind='grant'` | ✅ | `credit_ledger` | | 4 | интейк | 201, 3 главы, `character_count` ≈ 8759 | `chapter_count: 3` ✅; `character_count: 9677`, а 8759 — это `source_chars` ⚠ | ✅ по главам, **пин по знакам удовлетворён ДРУГИМ полем** | тело 201 + `books` | ⚠ **Пин строки 4 как он был написан — НЕ сошёлся, и это надо сказать прямо.** Я ждала ≈8759 от `character_count`, а он равен **9677**: это счёт ИНТЕЙКА по байтам потока, и платформа сама называет его приблизительным (`internal/pgstore/books.go`, и на проводе рядом едет `character_count_exact`). Точный счёт знаков живёт в `source_chars` = 8759 и сошёлся с манифестом до единицы. Дефекта нет, но пин был написан на поле, которое на него не отвечает. | 5 | срез совпал | `units_total` 4, по главам 2/1/1 | 4, по главам 2/1/1 | ✅ | `chapters`+`units` | | 6 | цена и бонд | `funded: true`, холд ≈2 241 629 | `funded: true`, `covers_all`, холд **2 241 631**, expected **84 122** | ✅ (мимо на 2 µUSD) | `run-options` | | 7 | старт | 202 + `bond_funded=t` | 202; `runs.bond_funded = t` | ✅ | ответ + `runs` | | 8 | ключи живы | первый вызов не отказан | первый вызов оплачен ($0.012044), отказов по авторизации 0 из 33 | ✅ | `request_log.err` пусто во всех 33 строках | | 9 | кадры | шесть типов | 50 кадров: `hello` 2 · `progress` 10 · `spend` 27 · `unit_done` 8 · `bank_stop` 1 · `finished` 2 | ✅ | `events.jsonl` | | 10 | банк-стоп | `awaiting_bank`, exit 3, непустой `proposed[]` | наступил в 04:39; exit 3; `proposed[]` = **69** термов, `terms[]` = 0 | ✅ | `runs` + `run_attempts` + копия сайдкара | | 11 | проекция банка | 0 до закрытия, >0 после | **0** в момент `awaiting_bank`; **69** строк (2 `approved`) после | ✅ | `GET /v0/books/{id}/bank` дважды | | 12 | дверь правок | preview → apply → файл решений | `changed: true`, оба решения `state: applied`, `mined-delta.yaml` 886 байт | ✅ | тело ответа + файл | | 13 | резюм | вторая попытка, exit 0 | попытка 2, exit 0, `last_seq` 22 | ✅ | `run_attempts` | | 14 | книга переведена | 4 юнита `translated` | 4 из 4 (контроль: юнитов книги — 4) | ✅ | `units` | | 15 | сборка | семь полей чисты | `complete: true`, `pending/withheld/incomplete/stale/ghost = 0`, `stale_unknown: false`, `config_drift: false`, `total_units: 4` | ✅ | stdout `tmctl build` | | 16 | выгрузка | `complete = t`, байты сходятся | txt: 54 151 байт, `complete = t`; epub: 20 187 байт, `complete = t` | ✅ | `exports` + `%{size_download}` | | 17 | ФАЙЛ | байты + структура | txt sha256 `c3dcf291…`; epub sha256 `720fd782…`, 7 записей zip, `nav` объявлен, 3 itemref, оглавление «Глава 1/2/3» | ✅ | разбор zip | | 18 | человек прочитал | связный русский | прочитан первый разворот главы 1 — связный русский текст, имена согласованы с подписанным банком | ✅ | цитата ниже | | 19 | деньги сошлись | четыре счёта + 0 резерваций | **семь** счётов сошлись на 419 424 µUSD (§5), открытых резерваций 0 при 2 резервациях этой учётки; ⚠ независимый среди семи — один | ✅ | таблица §5 | | 20 | ничего не реплеено | нет `local-*` | `distinct model_actual` = `deepseek-flash`, `deepseek-v4-pro`; `tm_hit = 1` у 6 строк, ВСЕ шесть — `banknote_export` второй попытки, все по $0 | ✅ | `request_log` | | 21 | расхождение форм | замер | **0 терминов из 40 отданы более чем одной формой** (контроль: канонических термов 69, найдено в тексте 40); прямой счёт по двум подписанным термам — 5 из 5 и 58 из 58 в одной форме, конкурирующих 0 (§16.8) | замер | `spread.py` + счёт по `book.txt` | **Первый абзац отгруженного файла (узел 18), дословно:** > Пусть тело погибнет — сердце демона не раскается. > — Фан Юань, покорно отдай Цикаду Весны и Осени, и я дарую тебе лёгкую смерть! Это связный русский текст. **О качестве перевода пак суждений не делает** (претрейн). ## 7. Счёт расхождения форм по ОТГРУЖЕННОМУ тексту (строка бэклога 406) `spread.py` над `tmctl export --json --pairs`: **69** канонических термов, у всех 69 исходник встречается в тексте, **40** найдены отрендеренными, и из этих сорока **НИ ОДИН не отдан более чем одной формой**. ### 7.1. ⛔ ЗНАМЕНАТЕЛЬ ЛЕЖАЛ В МОЕЙ ЖЕ УЛИКЕ, И БЕЗ НЕГО «0» НИЧЕГО НЕ ЗНАЧИЛ «0 из 40» отвечает на вопрос «нашёл ли прибор разнобой среди того, что видит». Настоящий вопрос другой: **что стало с теми терминами, про которые ИЗВЕСТНО, что они разошлись.** Ответ лежал в `evidence/bankstop-paid/project.db.bank-stop.txt`, который я сняла в момент стопа и не дочитала: движок печатает там `spread=N` и перечисляет черновые формы по КАЖДОМУ терму. **Распределение: `spread` 1 → 60 · 2 → 6 · 3 → 2 · 4 → 1** (контроль: записей `spread=` ровно 69, по числу термов). ⇒ **разошлись в черновике 9 терминов из 69.** Проверила все девять по отгруженному файлу (поиск по стемам слов, каждое попадание открыто глазами): | термин | черновик дал | подписано/сведено | в отгруженном файле | исход | |---|---|---|---|---| | `三转蛊师` | 2 формы | «гу-мастер третьего оборота» | 3 · конкурент 0 | ✅ одна форма | | `古月山寨` | **3** формы (поселение · городище · горная крепость) | «горная крепость Гуюэ» **подписана мной** | 5 (главы 1 и 2) · конкуренты 0, 0 | ✅ одна форма | | `甲等资质` | 3 формы (дар цзя · дарование первого ранга · талант класса А) | «талант первого разряда» | 6 · конкуренты 0, 0, 0 | ✅ одна форма | | `魔道巨擘` | 2 формы | «корифей пути демонов» | 2 · конкурент 0 | ✅ одна форма | | `高脚吊楼` | 2 формы | «дом на сваях» | **0**, зато «свайный дом» **×4** (главы **1 и 3**) | ⛔ в файле НЕ банковская форма | | `家老` | 2 формы | «старейшина рода» | 0; в тексте «старейшины» ×13 без «рода» | ⚠ короткая форма, последовательно | | `舅母` | 2 формы | «жена дяди по матери» | 0; в тексте «тётя» ×16 | ⚠ короткая форма, последовательно | | `舅父舅母` | 2 формы | «дядя по матери и его жена» | 0; «дядя с тётей» | ⚠ короткая форма, последовательно | | `开窍大典` | `spread=4`, перечислено **3** формы | «церемония открытия апертуры» | 9 (главы 1·2·3) + «великая церемония открытия апертуры» ×1 (**глава 1**) | ⛔ ДВЕ формы, и обе в одной главе | **⇒ 4 из 9 сведены и доехали · 4 из 9 отданы одной, но НЕ банковской формой · 1 из 9 отдан двумя.** ⚠ **Главы названы, как требует §4.5 п.2 пака, и они меняют смысл двух строк:** обе формы `开窍大典` живут в ОДНОЙ главе (читатель встречает разнобой на одной странице), а не-банковский «свайный дом» идёт через главы 1 и 3 — то есть последовательно по всей книге. ⚠ И самое важное из этой таблицы: **`高脚吊楼` — случай, где закон банка на проводе (`D39.104`: банк есть закон для ВСЕХ строк независимо от статуса) в тексте не виден.** Банк нёс «дом на сваях», файл несёт «свайный дом» четырежды. Доехал ли банк до промпта редактора на этих юнитах, по БД **не ответить**: у редакторской стадии нет своего счётчика подтверждённых инъекций (`n_exact_hits` и `injected_ids` заполняются на черновике, где глоссарий ещё пуст). **Замерено** — расхождение формы; **не установлено** — где именно оно возникло. ⛔ **Слепое пятно прибора я вскрыла, а не спрятала: 29 термов из 69 прибор не нашёл вовсе, и это его слабость, а не свойство книги.** Открыла глазами восемь таких единиц: `三转蛊师` отгружен как «гу-мастера третьего оборота», `丫头片子` — «девчонку», `仙师` — «бессмертными наставниками», `侧室` — «наложницей». Каноническая форма В ТЕКСТЕ ЕСТЬ, но склонена — а прибор её не видит. ⇒ «0 форм-расхождений» — утверждение о сорока термах, которые прибор видит, а не обо всех шестидесяти девяти. ⛔⛔ **И ПРИЧИНУ СЛЕПОТЫ Я НАЗВАЛА НЕВЕРНО — это доказано исполнением, не спором.** Первая редакция писала, что виновато правило сравнения общих префиксов (`same_rendering`), и заказывала следующей сессии переписать именно его. Критик полноты применил мой же рецепт к прибору на этих самых данных: **не вернулся НИ ОДИН из 29 потерянных термов** (осталось те же 40). Потому что `same_rendering` участвует только в СКЛАДЫВАНИИ уже найденных форм, а теряются они раньше — на ПОИСКЕ кандидатов: `spread.py` берёт кандидатов регуляркой `[А-ЯЁ][а-яёА-ЯЁ\-]+`, то есть **только слова С ЗАГЛАВНОЙ**. «гу-мастера», «девчонку», «наложницей» — строчные и отпадают ДО того, как правило сравнения вообще спросят. Починка генерации кандидатов (окна нужной длины, любой регистр) на тех же данных даёт **48 найденных вместо 40** — то есть слепое пятно действительно ЗДЕСЬ. ⇒ мой «0» держался в том числе на том, что прибор не смотрел, и правильный заказ следующему — §13, переписан. ⛔ **Но число расхождений у починенной копии я публиковать не стала, и вот почему — пере-снято своими руками, а не принято от критика.** Критик сообщил «шесть строк со `spread ≥ 2`»; моя реализация той же починки даёт **девять**, и все девять — мусор: оконный поиск глотает соседние слова и считает каждое окно отдельной формой («старейшины и», «старейшин Он», «Фан не», «глава рода он»). ⇒ **покрытие 40 → 48 — замер; счёт расхождений у починенного прибора — шум.** Улика: `checks/spread-candidate-fix-probe.txt`, где рядом стоят вывод фриза (0) и вывод копии (9) и назван недостающий кусок: окну нужен ФИЛЬТР, отвергающий лишние слова. ### 7.2. ⛔ ЗНАМЕНАТЕЛЬ ОКАЗАЛСЯ НЕ ОДИН: движок называет ВТОРУЮ популяцию, и я её пропустила `spread=N` отвечает на вопрос «разошлись ли ЧЕРНОВИКИ между собой». Он по построению НЕ ловит терм, у которого все черновики согласны — и все согласно расходятся с банком. Такие термы движок помечает отдельно, тем же файлом: **`INVENTED(no draft proposed it)` — форму банка не предложил ни один черновик.** Их **11 из 69**; четыре уже внутри девятки, **семь — вне её**, и вопрос у них ровно тот же. Проверила все семь (контроль: `INVENTED` встречается в стоп-таблице 11 раз при 69 записях `spread=`): | терм | банк | что в файле | исход | |---|---|---|---| | `丙等` | «третий разряд» | 3 (черновиковый «третий ранг» — 0) | ✅ банк | | `乙等资质` | «талант второго разряда» | 2 | ✅ банк | | `江城子` | «Цзянчэнцзы» | 1 («Река Цзянчэн» — 0) | ✅ банк | | `邪魔` | «злой демон» | 2 («злого демона», «злым демоном») | ✅ банк | | `话事人` | «распорядитель» | 1 («распорядители» — и черновик, и склонение банка) | ✅ банк | | **`凤雏`** | **«Молодой Феникс»** | **0**; в файле «Юный Феникс» ×1 (глава 2) — ЧЕРНОВИКОВАЯ форма | ⛔ не банк | | **`老嬷嬷`** | **«старая мамка»** | **0** («мамк\*» в файле 0); «нянька/няньку» ×2 (глава 3) | ⛔ не банк | ⛔ **Моя ошибка по дороге, названная: `邪魔` я сперва посчитала «не банковским».** Мой стеммер режет хвост по длине слова и на прилагательном «злой» промахивается мимо «злого/злым» — счёт дал 0. Открыла текст глазами: обе формы на месте. **Инструмент промахнулся в сторону ложной тревоги, а не ложного спокойствия**, но промахнулся. ⇒ **Объединённая популяция — 16 терминов из 69: 9 доехали формой банка · 6 отданы не-банковской формой · 1 отдан двумя.** И главное следствие для §13: **знаменатель у прибора по отгруженному тексту обязан быть ДВОЙНОЙ** — `spread ≥ 2` И `INVENTED`. На `spread` одном половина случаев не видна, включая два из шести. ## 8. Находки прогона — с разметкой уверенности | # | Находка | Где | Разметка | |---|---|---|---| | 1 | **Бинарь, собранный в ЛИНКОВАННОМ git-воркри, не несёт `vcs.*` ВООБЩЕ** (0 строк из 24), и правило «отказывать при `vcs.modified=true`» на нём выполняется всегда. Контроль: та же сборка из главного дерева даёт `vcs.revision` + `modified=true` | фриз-процедура пака | **замерено** | | 2 | **Книга, которую пайплайн переводит бесплатно, не может быть продана.** На $0-паре `step_max_usd = 0`, `ingest.Manifest.priced()` отказывает при `<= 0` ⇒ `409 not_priced`, старт недостижим; в логе ERROR «this book has no price projection, so it cannot be sold» | `internal/ingest/manifest.go:349` | **замерено** | | 3 | **Шаблон книги по рецепту стенда роняет КАЖДЫЙ старт `c1`:** без `langpack_root` движок отказывает с exit 10 «gates.terminology is enabled but this book cannot mine a bank». Ключа нет ни в `backend/example/book.yaml`, ни в рецепте `STACK_DECISIONS` | `internal/pipeline/runner.go:358` | **замерено** | | 4 | **Четверть денег купила пустоту** — 4 вызова из 33, $0.106472, все `finish_reason=length`; самый дорогой вызов книги ($0.071009) не дал ничего. Лечение в движке есть и сработало 4/4: перевыпуск с удвоенным потолком | §5.2 | **замерено** | | 5 | **Терминологический контур оплачен дважды** (до стопа и после резюма): $0.036554 + $0.028742 | §5.3 | **замерено** | | 6 | **`run_attempts.spend_micro_usd` — величина накопленная**; сумма по попыткам завышает цену книги на 38 % | §5 | **замерено** | | 7 | **Ответ старта прогона говорит `term_consistency_funded: false`, когда строка прогона уже несёт `bond_funded = t`** — и через минуту тот же прогон на `resume` отвечает `true`. Наблюдено на обоих прогонах (дымовом и платном) | `POST /v0/books/{id}/runs` | **замерено** (причина — **подозрение**: ответ, вероятно, рендерится из состояния ДО допуска) | | 8 | **`409 not_priced` сразу после интейка — гонка, а не дефект:** проекция цены материализуется асинхронно, через несколько секунд ручка отвечает нормально. Клиент, спросивший сразу, увидит отказ | `run-options` | **замерено** | | 9 | **Строка бэклога 224 воспроизведена живьём:** на банк-стопе `.bank.json` несёт `terms: []`, `total: 0` и `proposed[]` с **69** термами; платформа читает `terms` ⇒ экран подписи видит 0 ровно тогда, когда открыт ради этих 69 | улика `evidence/bankstop-paid/` | **замерено** | | 10 | **`TM_PLATFORM_ENGINE_CEILING_ARG` пустым старты не ломает** — у переменной есть дефолт `--ceiling-usd {{usd}}`; прогон прошёл целиком, пока она не задана | `internal/config/config.go:550` | **замерено** | | 11 | Резюм после банк-стопа **не берёт второй книжный бонд**: холд 2 081 420 = 2 241 631 − 160 211, то есть остаток бюджета этого прогона | `credit_ledger` id 15 | **замерено** | | 12 | Улика банк-стопа **уничтожается резюмом**: sha256 `.bank.json` `8fdd7d54…` на стопе → `0881055a…` после резюма (и после двери правок она ещё цела) | три sha подряд | **замерено** | | 13 | **Пин цены `deepseek-v4-flash` в `models.yaml` выше сегодняшней вендорской страницы** (вход 0.44 против 0.30, cache-hit 0.014 против 0.006, выход 1.32 против 1.20); у `pro` совпадает точно. Этот прогон по вендорским числам стоил бы $0.397246 вместо $0.419423 — пин завышает на **5.6 %** | §16.1, вендор-дока снята 11.09 | **замерено** (что пин стал выше страницы) + **прочитано** (сама страница) | | 14 | **`model_actual` = `deepseek-flash` на 28 строках из 33**, тогда как шлём `deepseek-v4-flash` и такого слага в вендорской прайс-таблице нет — нас считают под другим именем | `request_log`, две колонки | **замерено** (факт), **не установлено** (алиас или другая модель) | | 15 | **`err` пуст на всех 33 строках, включая четыре провальных** — признак отказа живёт только в `degraded`. Аудит, написанный через `err`, прочитает прогон как чистый | `request_log` | **замерено** | | 16 | **Три вызова вернули РОВНО 0 символов** (`length(response_text) = 0` в чекпойнтах): $0.094835 = 22.6 % прогона. Флор `min_max_tokens: 16000`, поднятый именно против этого, дал ОДИН такой вызов на 16.9 % счёта — больше, чем инцидент, ради которого его поднимали | `checkpoints`, `models.yaml` | **замерено** | | 17 | **`accept_rebill_micro = 2 241 631` на резюме** — согласие на пере-оплату всего бюджета прогона при фактической пере-покупке $0.028742 (разрыв 78×); спасли чекпойнты | `runs` | **замерено** | | 18 | **Σ `chunk_status.cost_usd` = $0.354127**, недостача — ровно терминология: таблица выглядит пятым путём счёта денег и им не является | `project.db` | **замерено** | | 19 | ⭐ **Подписанные термины доехали до файла:** «горная крепость Гуюэ» 5 из 5, конкурирующие черновые формы 0; «Фан Юань» 38 + 20 склонений, конкурирующих транслитераций 0 | §16.8, счёт по `book.txt` | **замерено** | ## 9. «Дыр нет» — замерено, а не промолчано Дверь выгрузки СТРОИТ ВСЕГДА (`--partial`, `D39.178`), поэтому «файл собрался» само по себе не значит ничего: неполная книга ушла бы тем же 200 и с теми же байтами, только с пометкой внутри. Пометка — данные языка ЧИТАТЕЛЯ (`configs/langpacks/ru/reader.txt`), и она проверяется на отсутствие вместе с контролем, который доказывает, что искали существующее: | файл | строк с `⚠` | «ещё не переведён» | «Перевод не завершён» | всего строк | |---|---|---|---|---| | **отгруженный `book.txt`** | **0** | **0** | **0** | 330 | | та же книга, собранная из её ПРЕД-ПРОГОННОЙ копии БД (контроль) | **5** | 4 | 1 | 20 | Контрольная сборка — тот же бинарь, тот же langpack, та же книга, только БД из `backups/` до прогона: `complete: false`, `pending_units: 4`, и файл честно говорит читателю: «⚠ Перевод не завершён: фрагментов с пропусками — 4 из 4. Каждый пропуск отмечен в тексте знаком ⚠». ⇒ ноль в первой строке таблицы — измеренный ноль, а не молчащий прибор. ## 10. Что НЕ удалось, не проверено и не измерено 0. ⛔ **ВЕРНЫЕ ЧИСЛА ПРИ НЕВЕРНОМ МЕТОДЕ — и это худшее, что случилось в моей работе, потому что оно не краснеет нигде.** Разбивку попыток я строила по ВРЕМЕНИ (`ts` против `started_at`), а `ts` в sqlite секундной гранулярности: `01:40:45` против `01:40:45.012584` — все шесть подстановок формально «раньше» старта своей же попытки. Числа при этом вышли правильные, по счастливому порядку строк. До этого тот же метод сломался грубее (пробел против `T` в разделителе дал 33/0), и я его «починила», не заметив, что он негоден в принципе. **Настоящий различитель лежал в той же строке и не требует арифметики вовсе: `trace_id` несёт номер попытки.** Нашёл критик чисел; сама бы я не нашла — верный результат не даёт повода перепроверить способ, которым он получен. 1. **`epubcheck` не гонялся.** `java` на хосте нет, поставить без sudo не пробовала (пак это прямо разрешает — ИСПР. 6). EPUB судится структурно: zip · `mimetype` · `container.xml` · `nav` · 3 `itemref` · оглавление. Валидность по спецификации EPUB — **не измерена**. 2. **Эскалация не исполнялась ни разу** — `escalation.budget_usd: 0` в боевом конфиге. Про её работу этот прогон не говорит НИЧЕГО. 3. **Гейт `coverage` выключен** (`gates.coverage.enabled: false`) — работали четыре гейта из пяти. Судейской стадии на этом пути нет вовсе. 4. **Одна книга, одна пара, три главы, четыре юнита.** Множитель «смета → факт» ×4.99 — это ОДНА точка (вторая в проекте). Двух точек мало для закона, и обе сняты на одном исходнике. 5. **Причина расхождения `term_consistency_funded` на старте не установлена** (находка 7): замерено, что ответ `POST /runs` говорит `false`, когда строка прогона уже несёт `bond_funded = t`; ПОЧЕМУ — подозрение (вероятно, ответ рендерится из состояния до допуска), кодом не подтверждено. 6. **Нужен ли второй заход терминологического контура — не выяснено.** Замерено, что он случился и сколько стоил; законен ли он после движения банка — вопрос к движку, не к этому прогону. 7. **Прибор расхождения форм видит 40 термов из 69** (§7). «0 расхождений» — утверждение о сорока. 8. **Книжный потолок ($1.25) и стоп-правила ни разу не срабатывали** — трата $0.419. Значит их поведение этим прогоном НЕ проверено: они стояли, но не стреляли. 9. **Ручка `POST /runs/{id}/stop` не трогалась вовсе.** 10. **Про качество перевода не сказано ничего и не может быть сказано** — 蛊真人 в претрейне. 11. **Дымовая фаза считала НОМИНАЛЬНЫЕ деньги.** Локальной модели дана цена (иначе книга не продаётся — находка 2), поэтому суммы дымового аккаунта — это арифметика механизма, а не траты. Платный аккаунт заведён отдельно, и в денежную сверку §5 дымовые строки не входят. 12. ⛔ **НИ ОДНО HTTP-ТЕЛО ПЛАТНОГО ПРОГОНА НЕ СОХРАНЕНО, и я узнала это от критика, а не от себя.** Под `evidence/` 61 файл, и ни в одном нет `covers_all`, `term_consistency_funded`, `affordable_chapters`, `accepted`, `ordered_chapters`. Уцелели только `capabilities.json` и `bank-after.json`. ⇒ висят без носителя: ответ `run-options` (узел 6; сам ХОЛД предъявлен строкой `reservations`, а тело — нет) · `202` старта (узлы 7 и 13; в `runs` и логе демона есть «run admitted … ordered_chapters:3» — это сильнее нуля, но не то тело) · `changed: true` и `state: applied` двери правок (узел 12; уцелел ЗАПРОС `stand/corrections.json`, ответ — нет) · и целиком **находка 7 §8**, которая ТОЛЬКО из тела ответа и состоит. Драйвер печатал их на экран и не писал в файл — это дефект моего харнесса, а не прогона, и чинить его надо в `drive.sh` (каждый ответ ручки — в `evidence/http/<шаг>.json`). 13. **Слабость МОЕГО прибора живости:** `drive.sh watch` печатал `max(last_seq)` по попыткам, и во время второй попытки число выглядело замершим на 28 (значении первой), пока работа шла. Читать надо по попыткам — что я и делала во второй половине вахты. ## 11. Что я сама считаю слабым местом этого прогона **Первое и главное: счёт расхождения форм — самая новая и самая слабая цифра отчёта.** Всё остальное здесь предъявлено механизмом, который писали не в этом паке; прибор §7 написан мной за час, и его правило сравнения форм разваливается ровно там, где русский язык делает то, что делает всегда, — склоняет первое слово. Его «0» я бы не понесла дальше без пере-строенного правила. **Второе: «сошлось семью путями» звучит сильнее, чем есть.** Шесть из семи путей — одна и та же цепочка: движок посчитал, движок записал, платформа материализовала из его же потока. Настоящая независимость в этом отчёте одна — **скачанные байты и их sha256**; провайдерского биллинга у нас нет вовсе. ⛔ И вторую половину этой фразы я тоже переписываю после критика чисел: «$0.419424» — это даже не то, что нам сказал провайдер. Провайдер сообщил ТОКЕНЫ; доллары мы умножили сами, на свой пин, и пин на flash оказался выше вендорской страницы (§16.1). **Настоящее число — «столько-то токенов»; доллары в этом отчёте — наша модель поверх них.** **Третье: прогон прошёл слишком гладко, чтобы проверить свои же защиты.** Стоп-правила, книжный потолок, отказ допуска — всё это стояло заряженным и ни разу не выстрелило. Я проверила их подсадками до денег (§4), но на боевой руке они остались непроверенными, и это надо помнить, а не записывать себе в актив. ## 12. Девиации от пака — объявлены, не подразумеваются 1. **Фриз-дерево — локальный КЛОН, а не `git worktree add`.** Причина замерена: бинарь из линкованного воркри несёт **0** строк `vcs.*` из 24 (контроль: та же сборка из главного дерева даёт `vcs.revision` и `vcs.modified=true`), и правило «отказать при `vcs.modified=true`» на нём выполняется ВСЕГДА. Клон даёт настоящий каталог `.git` и штамп с зубами. Санкционировано оркестратором до первого цента. 2. **Драйвер правлен ПОСЛЕ первого фриз-коммита** — второй фриз `44b33d9`: гейт должен был требовать слушателя на `127.0.0.1:11434`, что верно для дыма и ВРЕДНО для платной фазы (чужая заглушка, поглощающая вызовы, — ровно то, чего нельзя допустить). ⇒ бинари стоят на `b0f5d89`, харнесс — на `44b33d9`. Правка сделана ДО первого платного вызова; форма соблюдена (новый фриз), но расхождение двух sha названо здесь, а не подразумевается. 3. **Дымовой стенд дал локальной модели номинальную цену** (`models-smoke.yaml`, отличается от боевого `models.yaml` РОВНО одной строкой). Без этого дым через HTTP-дверь невозможен — находка 2. Платная фаза шла на боевом `models.yaml` фриза, и платный аккаунт заведён отдельно от дымового. 4. **`epubcheck` не гонялся** — `java` на хосте нет (§10 п.1), пак это разрешает. 5. ⛔ **ДВА МОИХ ИНСТРУМЕНТА СОБРАНЫ НЕ ИЗ ФРИЗА, и гейт их не проверяет.** `bin/stub` и `bin/zeropipe` несут `vcs.revision=468ed4d…` и **`vcs.modified=true`** (контроль: три гейтованных бинаря — `b0f5d89…` и `false`): они собраны из ГЛАВНОГО дерева, где лежит их исходник, а `step_gate` смотрит только на `tmctl · tmplatformd · tmplatformctl` (`drive.sh`). Денег это не касается — заглушка работала только в дыму, а `zeropipe` $0-рендер; но пин фазы 0 «0 достижимых платных моделей» произведён бинарём из грязного дерева, и это надо знать. ⚠ Смягчающее и проверяемое: ИСХОДНИК обоих закоммичен фризом `be53cc8`, то есть код пинен, даже если штамп бинаря — нет. Нашёл критик полноты. 6. ⚠ **У фриза ДВА разных числа, и путать их нельзя.** `b0f5d89…` — sha КОДА, которым собраны бинари (HEAD клона). `be53cc8` — КОММИТ, которым заморожены пре-регистрация и инструменты; `44b33d9` — второй такой коммит. Первое отвечает на «что исполнялось», второе и третье — на «чем судили». ⚠ И третье число, которое встречалось на стенде: `~/tm-coldrun-a/FROZEN_SHA.txt` до конца смены нёс `88f1344…` — sha БРОШЕННОГО воркри, из которого для прогона не собрано ничего. Гейт этот файл не читает, так что он никого не обманул; исправлен и снабжён объяснением. ## 13. Заказ следующему: как чинить прибор расхождения форм ⛔ **ПЕРВАЯ РЕДАКЦИЯ ЭТОГО ЗАКАЗА БИЛА МИМО, и это проверено исполнением, а не спором.** Она велела переписать правило сравнения (`same_rendering`) на пословную стеммизацию. Критик применил рецепт к прибору на боевых данных: **не вернулся ни один из 29 потерянных термов.** Правило сравнения решает, СКЛАДЫВАТЬ ли две уже найденные формы; термы теряются раньше. **Чинить надо ГЕНЕРАЦИЮ КАНДИДАТОВ.** `spread.py` ищет формы регуляркой `[А-ЯЁ][а-яёА-ЯЁ\-]+(?:\s+[А-ЯЁ][а-яёА-ЯЁ\-]+)*` — то есть **только слова с ЗАГЛАВНОЙ буквы**. Имена она ловит, а «гу-мастера третьего оборота», «девчонку», «наложницей» — нет, потому что они строчные. Правильный поиск: брать из целевого текста **окна длиной в число слов известной передачи, в любом регистре**, и сравнивать их с известными формами — и только ПОТОМ складывать найденное. На тех же данных это даёт **48 найденных термов вместо 40** (замерено мной, `checks/spread-candidate-fix-probe.txt`). ⛔ **И сразу вторая половина рецепта, без которой первая вредна:** окну нужен ФИЛЬТР, отвергающий окна, чьи лишние слова не входят в передачу. Без него та же починка печатает девять «расхождений», из которых ни одно не расхождение («старейшины и», «Фан не»). **И второе, что заказ обязан нести: знаменатель, и он ДВОЙНОЙ.** Движок уже печатает по каждому терму и `spread=N` со списком черновых форм, и пометку `INVENTED(no draft proposed it)` (`.bank-stop.txt`), то есть САМ называет обе популяции вопроса. Прибор по отгруженному тексту должен считать не «сколько разнобоя я вижу», а **«что стало с теми, про кого известно, что форма банка и форма черновика могли разойтись»** — на этой книге 9 + 7 = **16 из 69**. ⚠ На одном `spread` половина случаев невидима: `凤雏` и `老嬷嬷` отданы черновиковой формой при `spread=1` (§7.2), и прибор, построенный на одном знаменателе, их не увидит никогда. **Третье — устойчивость.** Нынешний «0» не пере-снят ни при одном другом пороге складывания; под пословным правилом терм `沈翠` распадается на «Шэнь»/«Шэнь Цуй» и заголовочное число становится 1. Прибор обязан печатать свой порог и давать число при двух-трёх значениях. Самотест уже несёт три клетки (вторая форма ловится, склонение не двоится, неотрендеренный терм даёт ноль). К ним добавить две: **строчную многословную передачу** (на которой нынешний прибор слеп) и **терм из `spread ≥ 2` движка**, чтобы знаменатель тоже был под пином. ## 14. Разбор после критика полноты — что он нашёл и что с этим стало Критик полноты прошёл по паку и по артефактам. Три его находки были про ПРИБОР и потребовали работы, а не текста; они исполнены здесь. ### 14.1. ⛔ `tm_hit` — разбивка по попыткам, которой требует ИСПР. 20 (исполнена) Сборщик печатал СУММУ попаданий в чекпойнты (`collect.py`: `tm_hit=1 …: 6 (control: rows = 33)`), а ИСПР. 20 называет единственным различителем именно РАЗБИВКУ: попадание ВНУТРИ одной попытки — подозрение (прогон переигрывает сам себя и мог измерить пустоту), попадание второй попытки в чекпойнты первой — норма резюма. Разбивка снята запросом к `project.db` (`mode=ro`): | попытка (по `trace_id`, который несёт её номер) | строк | `tm_hit = 1` | Σ cost | |---|---|---|---| | `tm-stream-run_FTHA7S6SUCJIXS2A-1` | 16 | **0** | $0.160210 | | `tm-stream-run_FTHA7S6SUCJIXS2A-2` | 17 | **6** | $0.259213 | | контроль: всего | 33 | 6 | $0.419423 | ⇒ **Внутри попытки не переиграно НИЧЕГО; все шесть попаданий — вторая попытка, читающая чекпойнты первой, и все шесть стоят $0.** Сверх того: суммы по попыткам ($0.160210 и $0.259213) ложатся на две строки `settlement` платного аккаунта (160 211 и 259 213 µUSD) — с тем же ceil на первой (+1 µUSD), который §5 объясняет; на второй он совпал в ноль. Это не «совпадение», а сходимость с названным правилом округления. ⛔ **Две мои ошибки в методе этой самой таблицы, обе названы, потому что они одного класса — «верный прогон при неверной гипотезе».** (1) Сначала я делила попытки сравнением `ts < '2026-09-11T01:40:45'`, а колонка хранит `'2026-09-11 01:40:45'` — с ПРОБЕЛОМ, который сортируется раньше `T`: все 33 строки оказались «меньше границы», и разбивка напечатала 33/0 — правдоподобно и неверно. (2) Починив разделитель, я всё ещё делила по ВРЕМЕНИ, а `ts` в sqlite секундной гранулярности: `01:40:45` против `started_at` попытки `01:40:45.012584` — то есть все шесть подстановок буквально «раньше» её старта. Числа при этом вышли правильные по счастливой случайности порядка. **Настоящий различитель лежал в той же строке и не требует арифметики вовсе: `trace_id` НЕСЁТ номер попытки.** Таблица выше пере-снята по нему; вторую ошибку нашёл критик чисел, не я. ### 14.2. ⛔ Сборщик считает деньги ПО ВСЕМ учёткам сразу `collect.py` спрашивает `credit_ledger` и `reservations` без фильтра по пользователю — поэтому в его разделе C стоит `grants: 5000000 µUSD` (два гранта по $2.50: дымовой учётке и платной) и «резерваций всех состояний 5» (три дымовых плюс две боевых). **Пре-рег §1 развёл учётки именно для того, чтобы этого не было, а прибор их всё равно смешал.** Числа §5 этого отчёта сняты ОТДЕЛЬНЫМ запросом с фильтром `where user_id = 'u_ZWUVTETPZL2D6LML'` (платная учётка) и потому верны; исправлять замороженный прибор после денег я не стала — **гап назван, и он в его же зоне ответственности.** Контроль, доказывающий, что речь о смешении, а не о лишних деньгах: у платной учётки ровно **один** грант 2 500 000 µUSD и ровно **две** резервации, обе `settled`. ### 14.3. Шесть строк `banknote_export` — не ошибка, а бесплатный реплей В списке «строк с `err` или `finish_reason ≠ stop`» их 6 из 10. Все шесть: `tm_hit = 1`, `cost_usd = 0`, вторая попытка, `ok = 1`. Это извлечение банкнотного блока, взятое из чекпойнта первой попытки, а не неудачный вызов. ⇒ **денежный класс §5 п.4 — это ЧЕТЫРЕ строки `length`, а не десять**; остальные шесть названы здесь, чтобы никто не считал их дважды. ### 14.4. Прибор расхождения форм — второй прогон, на материале, названном паком §4.0 п.5 велит строить прибор на `coldrun-v16`. Строился и самотестировался он раньше, а на этом материале прогнан теперь (копия проекта, читающий верб, $0): **14 юнитов, 11 pending, 3 с текстом** (это и есть история 31.08: три юнита из четырнадцати до остановки снапшот-гардом), 50 канонических термов, 49 с исходником в тексте, **11 отрендеренных, 0 отданных более чем одной формой**. ⛔ **И снова моя ошибка по дороге, названная:** первый заход я сделала с `book_id`, который придумала сама (`v16-readonly`), — а строки БД ключуются ИСХОДНЫМ `guzhenren-coldrun-v16`. Экспорт честно отдал 14 pending и 0 текста, и это выглядело как «в артефакте нет текста». Правильный `book_id` дал 3 юнита с текстом. **Ноль отрендеренных термов был свойством моего запроса, а не книги** — ровно тот класс ошибки, ради которого рядом с нулём печатается контрольная величина. ### 14.5. Артефакты проверок, которые раньше жили только в моём выводе Сохранены в `~/tm-coldrun-a/evidence/checks/` — **тринадцать файлов, список полный**: `gate-paid.txt` (гейт платной фазы целиком, включая «port 11434: nothing listens (control: 7 listeners on this host)») · `gate-negative-controls.txt` (**три подсадки и текст каждого отказа**: чужая ревизия с грязным деревом · бинарь БЕЗ штампа · неверный ожидаемый sha) · `zeropipe-count.txt` (8 достижимых платных моделей в `pipeline-c1.yaml` против 0 в рендере) · `pipeline-zero-recheck.yaml` (сам рендер) · `spread-selftest.txt` · `spread-v16.txt` · `poscontrol-collector.txt` (**подсадка банка/юнита/ резервации с откатом — сборщик краснеет по трём осям**) · `capabilities.json` (непустой `export_formats`) · `bank-after.json` · `epub-structure.txt` (7 записей, `nav`, 3 `itemref`, `testzip() = None`) · `run_FTHA7S6SUCJIXS2A-1.exit` и `-2.exit` (маркеры systemd) · `spread-candidate-fix-probe.txt` (моя проверка починки прибора: покрытие растёт, счёт расхождений — шум). Плюс `evidence/poscontrol-paid-holes/` — контрольная сборка книги с дырами. ### 14.6. Формула холда закрыта ТОЧНО, и расхождение пре-рега в 2 µUSD объяснено Пре-рег предсказал холд 2 241 629 µUSD, факт — **2 241 631**. Разница не в формуле, а в слагаемом: я взяла ожидание из суммы глав манифеста (84 120), а платформа хранит его как `expected_micro_usd − book_once_micro_usd` = **84 121**. Проверено запросом к её собственной строке: ``` ⌈84 121 × 1.25⌉ + step_max 136 479 + book_once 2 000 000 = 105 152 + 136 479 + 2 000 000 = 2 241 631 ``` — и ровно 2 241 631 стоит в `reservations.amount_micro_usd` первой попытки. ⇒ **холд = ⌈ожидание текста × 1.25⌉ + `step_max` + книжный бонд**, сходится до микро-доллара; формула пака «грант ≥ 1.25×expected + step_max + 2.00» подтверждена точным замером, а не приблизительно. ### 14.8. Третий носитель следа — systemd-маркеры выхода §4.3 называет третьим носителем `/runs/-.exit` — маркер ЮНИТА systemd, а не переводческого юнита. Оба сняты и сохранены (контроль: маркеров в каталоге состояния всего 5, из них два — этого прогона): ``` run_FTHA7S6SUCJIXS2A-1.exit {"result":"exit-code","code":"exited","status":"3", "at":"…01:38:48Z"} run_FTHA7S6SUCJIXS2A-2.exit {"result":"success", "code":"exited","status":"0", "at":"…01:54:44Z"} ``` ⇒ Банк-стоп (3) и успех (0) подтверждены ОПЕРАЦИОННОЙ СИСТЕМОЙ, а не только строкой `run_attempts`, которую платформа пишет сама. Это единственное место в отчёте, где выход прогона предъявлен источником вне её собственной цепочки. ### 14.9. Файл — это ТОТ САМЫЙ текст, а не его пересказ Последняя сверка, которой в первой редакции не было: каждый из четырёх переведённых юнитов взят из `units.target` платформы и найден в скачанном `book.txt` **дословно, подстрокой** — 5761, 5558, 10 206 и 8361 знак, все четыре `True`. Арифметика сходится: Σ юнитов 29 886 знаков, файл 29 913, разница **27** — заголовок книги, три заголовка глав и пустые строки между ними. ⇒ между тем, что заплачено, и тем, что скачано, ничего не потеряно и ничего не добавлено. ### 14.10. Журнал ДВИЖКА сохранён — без него половина §5.2 висела в воздухе Критик полноты заметил: числа `max_tokens` на входе (8496 / 16000), потолки перевыпуска (16992 / 32000), строка «stage flagged, regenerating with a larger budget … reason=empty» и отказ по `langpack_root` (exit 10) жили только в моём выводе — в `evidence/` их не было. Журнал юнитов выгружен и лежит в `evidence/journal/`: `run-attempt-1.log` (170 строк), `run-attempt-2.log` (246), `smoke-langpack-exit10.log`. Проверено счётом, что цитируемое там есть: `next_max_tokens=16992` — 3 (ровно три черновых перевыпуска), `next_max_tokens=32000` — 1 (редакторский), `max_tokens=8496` — 6, `max_tokens=16000` — 4, «bank-mining stopped on new terms» — 1, «cannot mine a bank» — 1. ⭐ И журнал подтверждает разметку флагов из БД **независимым носителем**: ровно четыре события «stage flagged» — `empty` (draft ch1/0) · **`length`** (draft ch2/1) · `empty` (draft ch3/0) · `empty` (edit ch2/0), то есть та же четвёрка и те же причины, что в колонке `degraded`. ### 14.7. «Ноль до закрытия» предъявлен артефактом, а не выводом на экране Критик справедливо заметил, что нуля проекции банка в момент `awaiting_bank` в файлах не осталось. Он предъявлен сильнее — тем самым артефактом, ИЗ КОТОРОГО платформа этот ноль и берёт: `evidence/bankstop-paid/project.db.bank.json`, снятый в 04:39:43, несёт `terms: []`, `total: 0`, `signed: 0` при `proposed[]` из **69** термов. Платформенный `SaveBank` читает `terms`, поэтому её ноль — не пропажа, а точная проекция того, что ей дали. После закрытия прогона: `bank-after.json` показывает `total: 69`, `signed: 2` — те самые два терма, подписанные через дверь. ## 15. Где лежат артефакты | Что | Где | В git? | |---|---|---| | Инструменты (драйвер, сборщик следов, прибор форм, $0-заглушка, рендер $0-конфига) | `eval/door_to_file/` | **да**, фризы `be53cc8` и `44b33d9` | | Пре-регистрация и этот разбор | `docs/experiments/24-door-to-file.md` | пре-рег — да (`be53cc8`); результаты лендит оркестратор | | **Сырые платные выходы**: отгруженные `book.txt`/`book.epub`, улика банк-стопа, полный след, счёт форм, сохранённые проверки | `books/gu-zhenren/door-to-file/` | отдельный репозиторий книг, **НЕ коммитила** — лендит владелец/оркестратор | | Стенд, копии БД, контрольные сборки, дымовая фаза | `~/tm-coldrun-a/` (вне git, но не в `/tmp` — переживает уборку скретчпадов) | нет | Стенд оставлен ПОДНЯТЫМ для проверки: `tmplatformd` на `127.0.0.1:8097` (pid записан в `~/tm-coldrun-a/stand/daemon.pid`), база `tm_coldrun_door` в кластере `/tmp:55433`, клон фриза — `~/tm-coldrun-a/freeze`. Гасить по слову оркестратора; убивать ТОЛЬКО по pid. ## 16. Разбор после критика ЧИСЕЛ — что он опроверг и что добавил Второй критик пере-вывел каждое число из сырья своими командами. Две мои формулировки он опроверг (обе сняты выше по тексту), и ещё девять вещей нашёл сам. Ниже — то, что я перепроверила своими руками, а не приняла на слово. ### 16.1. ⛔ ЦЕНА РАБОТЫ ПОСЧИТАНА ПО ПИНУ, А ПИН ПРОТУХ НА FLASH Критик заметил, что $0.419424 — не счёт провайдера, а результат прайса из `models.yaml`, и что DeepSeek держит пиковую и офф-пиковую цену. Проверено по вендор-доке (`api-docs.deepseek.com/quick_start/pricing`, снято 11.09.2026, два независимых захода дали одни и те же числа): > «Off-peak rates are half of the peak rates. **Peak hours are 01:00 - 04:00 and 06:00 - 10:00 UTC, > Monday through Friday** (all other hours are off-peak).» **Прогон шёл 01:29–01:54 UTC в пятницу ⇒ ВНУТРИ пикового окна**, и пиковый прайс для него — верное основание. Это снимает половину тревоги. Но вторая половина осталась и она дороже: | за 1M токенов | наш пин `models.yaml` | вендор сегодня (пик) | расхождение | |---|---|---|---| | flash, вход (cache miss) | **0.44** | **0.30** | ×1.47 | | flash, вход (cache hit) | **0.014** | **0.006** | ×2.33 | | flash, выход | **1.32** | **1.20** | ×1.10 | | pro, вход / cache-hit / выход | 1.32 / 0.044 / 3.96 | 1.32 / 0.044 / 3.96 | **совпадает точно** | **Пере-считала все 27 настоящих вызовов дважды.** По пину сходится с движком **до нуля микро-долларов** (арифметика движка безупречна). По живым вендорским числам тот же прогон стоил бы **$0.397246**: ``` pinned $0.419423 (flash 22 вызова $0.188953 · pro 5 вызовов $0.230471) vendor $0.397246 (flash $0.166776 · pro $0.230471) ⇒ пин ЗАВЫШАЕТ этот прогон на $0.022177 = 5.6 %; по flash — ×1.133, по pro — ×1.000 ``` ⇒ **«$0.419424» — это то, что насчитала НАША модель цен; провайдер по своей сегодняшней странице взял бы ≈$0.397.** Разницу не чиню (не моя зона и не мой пак), но называю: у пина flash есть дата («ПИК с 16.08.2026 16:00 UTC», `models.yaml:218`), и за три недели вендор его сдвинул вниз. ### 16.2. ⛔ СЛАГ, КОТОРОГО НЕТ В НАШЕМ РЕЕСТРЕ `model_requested` = `deepseek-v4-flash` (наш конфиг), `model_actual` = **`deepseek-flash`** — на **28** строках из 33 (у pro обе колонки совпадают). Слага `deepseek-flash` в `models.yaml` нет вовсе, а вендорская прайс-страница называет модель именно так. То есть **имя, под которым нам отвечают и по которому вендор нас считает, отличается от имени, которое мы шлём и по которому считаем себя.** Правило проекта здесь однозначно — «слаг живой ≠ модель та же», — и это вопрос бэкенду, а не вывод прогона. **Замерено**: счёт строк по паре колонок. **Не установлено**: алиас это или другая модель. ### 16.3. Отказы не видны через `err` — только через `degraded` `err` пуст на **всех 33** строках, включая четыре провальные (контроль: `err <> ''` → 0). Признак отказа живёт в колонке `degraded`: `empty` ×3, `length` ×1, пусто ×29. ⇒ **любой аудит денег или отказов, написанный через `err`, прочитает этот прогон как идеально чистый** — и не увидит четверти траты. Мой сборщик спасся тем, что смотрел на `finish_reason` и `ok`, а не на `err`. ### 16.4. Три вызова из четырёх вернули РОВНО НОЛЬ символов В `checkpoints` у их `request_hash` `length(response_text)`: id 1 → **0**, id 7 → **0**, id 31 → **0**, id 5 → 4203 (обрезок, выброшен и перекуплен). ⇒ **$0.094835 = 22.6 % прогона куплено за ноль знаков**, и ещё 2.8 % — за выброшенный обрезок. У id 31 ноль знаков стоил **16 000 оплаченных выходных токенов**. ⚠ **И самое неприятное: флор, поднятый ИМЕННО против этого, не помог.** `models.yaml` у `deepseek-v4-pro` несёт `min_max_tokens: 16000`, поднятый с 8000 после прошлого инцидента, и рядом записана его цена: «$0.0177, то есть 15.5 % всего счёта прогона, ушло в две выброшенные генерации». Здесь ОДИН вызов на поднятом флоре сжёг 16 000 токенов и вернул ноль: **$0.071009 = 16.9 % счёта — доля БОЛЬШЕ, чем у инцидента, ради которого флор поднимали.** ### 16.5. `chunk_status` — четвёртый «путь счёта», который был бы неверен Σ `chunk_status.cost_usd` = **0.354127** против 0.419423. Недостача — **ровно** терминологический контур ($0.065297): в `chunk_status` 10 строк и только стадии `draft` и `edit`, строк терминологии там нет по построению. Таблица напрашивается как ещё один «независимый» счёт денег и им не является. ### 16.6. Резюм выдал согласие на пере-оплату ВСЕГО бюджета `runs.resnapshot = true`, `runs.accept_rebill_micro = **2 241 631**` ($2.24) — санкция на пере-покупку всего, что прогон уже купил. Фактически пере-куплено $0.028742 (терминология), потому что все шесть черновых единиц вернулись из чекпойнтов бесплатно. **Разрыв между выданным согласием и потраченным — 78×.** Промахнись хэши промптов — черновая волна была бы куплена заново по полной цене, и согласие на это уже стояло в строке. **Замерено**; законен ли такой размах согласия — вопрос не этого прогона. ### 16.7. Мелочи, каждая с числом - **`bank_stop_presented` = 70 при 69 предложенных** (и 69 строках `glossary`, и 69 в `bank_terms`). Лишняя предъявленная поверхность — `方源的` (терм + частица 的), строки глоссария за ней нет. «Предъявлено» и «предложено» — разные величины, и одна на единицу больше. - **6 черновых чанков → 4 редакторских юнита** — это нарезка, а не потеря: манифест несёт `units_total 2 / chunks 2` у главы 1 и `units_total 1 / chunks 2` у глав 2 и 3. Проверено и по тексту: все четыре `units.target` входят в `book.txt` дословно. - **Округление ВВЕРХ на каждом кадре `spend`, а не только в конце** (12044.12 → 12045 и так далее). На этом прогоне пере-плата +0.7 µUSD, но знак у неё постоянный. - **Метод, которым я делила попытки, был слабым.** Я сравнивала `ts` с `started_at`, а `ts` в sqlite секундной гранулярности: `01:40:45` против `01:40:45.012584` — буквальное сравнение уводит все шесть подстановок в первую попытку. Правильный различитель лежал в той же строке: **`trace_id` несёт номер попытки** (`tm-stream-run_FTHA7S6SUCJIXS2A-2`). Вывод §14.1 верен, метод в нём заменён на `trace_id`. ### 16.8. ⭐ ПОДПИСЬ ДОЕХАЛА ДО ТЕКСТА — и это лучший результат прогона после самого файла Вопрос, который я не задала, а критик задал: **дошли ли подписанные термины до отгруженного файла?** Проверено по `book.txt` прямым счётом: | термин | что было в ЧЕРНОВИКЕ | что в ОТГРУЖЕННОМ файле | |---|---|---| | `古月山寨` | **ТРИ** формы, `spread=3`: «поселение Гуюэ» ×1 · «городище Гуюэ» ×1 · «горная крепость Гуюэ» ×1 (`bank-stop.txt`) | **«(г/Г)орная крепость Гуюэ» — 5 из 5**; «поселение Гуюэ» — **0**, «городище Гуюэ» — **0** | | `方源` | «Фан Юань» | «Фан Юань» 38 + склонения 20 (Юаня 17 · Юаню 2 · Юанем 1); конкурирующих транслитераций («Фань Юань», `Fang Yuan`) — **0** | ⇒ **контур банка со стопом и подписью отработал свои $0.0287 живьём:** разнобой, который черновик дал по месту, в файле читателя отсутствует. Это НЕ суждение о качестве перевода — это счёт форм. ⛔ **Две поправки к этому же абзацу, обе от критика полноты.** (1) Черновик дал `古月山寨` не две формы, а **три** — я цитировала `other proposals` карты подписи (две) и не открыла `bank-stop.txt` того же прогона, где стоит `spread=3` и перечислены три черновика. ⚠ И тот же класс расхождения нашёлся ВНУТРИ одного артефакта: у `开窍大典` стоит `spread=4`, а в строке `drafts:` рядом перечислены **три** формы. Проверено по всем 69: у **68** термов `spread=N` равно числу перечисленных форм до единицы, и `开窍大典` — единственное исключение. **Дефект это или разные определения — НЕ УСТАНОВЛЕНО**; для §7.1 я брала число перечисленных форм, а не счётчик. Правильное число делает вывод СИЛЬНЕЕ, а расхождение двух артефактов движка по одному терму (`other proposals` = 2 против `drafts:` = 3) — **разные определения или дефект, НЕ УСТАНОВЛЕНО**. (2) Обобщать этот успех на книгу нельзя: полный счёт по всем девяти разошедшимся терминам (§7.1) даёт 4 доехавших, 4 отданных не-банковской формой и 1 отданный двумя. ## 17. Что сказал критик по КАЖДОЙ фазе Критиков было два (оба — отдельные сессии-опровергатели, модель задана явно): **критик полноты** шёл по паку пункт за пунктом против артефактов, **критик чисел** пере-выводил каждое число из сырья своими командами. Ниже — по фазам, как требует §5 пака. | Фаза | Что сказал критик | Что с этим стало | |---|---|---| | **0 — $0-дым** | Позитивные контроли исполнены (подсадка банка/юнита/резервации, книга с дырой, две подсадки в гейт бинарей, самотест прибора форм), НО их вывод жил только в моём экране, а не в файлах. Прибор форм пинен на дымовых парах, а не на материале, названном §4.0 п.5 | Все контроли пере-сняты и сохранены в `evidence/checks/` (§14.5); прибор прогнан на `coldrun-v16` (§14.4), и по дороге вскрыта моя же ошибка с чужим `book_id` | | **1 — предусловия** | Закрыто артефактом: манифест до фриза, контраст-артефакт по sha, пять переменных (одна из пятёрки опровергнута — у `CEILING_ARG` есть дефолт), langpack-находка | Без изменений; находки 2 и 3 (§8) заведены оркестратором рядами 411 и 412 | | **2 — фриз** | Форма верна: клон, три условия гейта, `/tmp/.git`, штамп по каждой попытке. Но девиация «драйвер правлен ПОСЛЕ фриза» (`44b33d9`) в отчёте не названа | Названа отдельным пунктом §12.2 с обоими sha | | **3 — пре-регистрация** | Таблица есть, но **исхода не было ни у одной строки** (отчёт на момент проверки состоял из одного пре-рега) | Написан §6 — исход у всех 21 строки, с фактом и предъявлением | | **4 — платный прогон** | Числа денег верны, НО: «419 423 по движку» — мой floor, а не число движка; «33 вызова» — это 33 строки при 27 вызовах; разбивка `tm_hit` по попыткам не построена, хотя ИСПР. 20 называет её единственным различителем; агрегаты денег в сборщике не отфильтрованы по учётке | Две формулировки сняты (§5, §16), разбивка построена и пере-снята по `trace_id` (§14.1), гап сборщика назван (§14.2) | | **5 — $0-разбор** | Цена посчитана по ПИНУ, а у DeepSeek есть пик/офф-пик — «прогон стоил» без вендор-сверки не заслужено; `err` пуст на всех строках; `chunk_status` недобирает терминологию; согласие на пере-оплату выдано на весь бюджет; не спрошено, доехала ли подпись до текста | Вендор-дока снята (§16.1): окно пиковое, основание верное, но пин flash протух на 13 % — новая находка; остальное внесено §16.3–16.8, включая ⭐ проверку подписи по тексту | ### 17.1. Итог ТРЕТЬЕГО прохода — круги НЕ сошлись, и я этого не прячу Третий проход шёл по ЗАМОРОЖЕННОМУ документу (857 строк, sha256 `feac01e0…c8059`; критик сверил sha дважды — в начале и в конце). Он подтвердил внесение восьми находок предыдущих проходов «точно» и пере-снял §7.1 целиком: девятка верна, исход по каждой из девяти клеток сошёлся. **И дал четыре новые находки:** 1. **Второй знаменатель `INVENTED`** — 11 термов, 7 вне девятки, и среди них ещё ДВА случая не-банковской формы в файле. Внесено: §7.2, популяция стала 16, исход 9 · 6 · 1, §13 требует двойного знаменателя. 2. **«Шесть строк `spread ≥ 2`» — число без носителя.** Я процитировала его от критика чисел дважды; моя собственная проверка даёт девять, и все девять — шум. Внесено: §7 и §13 переписаны, улика — `checks/spread-candidate-fix-probe.txt`. 3. **`spread=4` у `开窍大典` при трёх перечисленных формах** — единственное расхождение счётчика со списком на 69 термов. Внесено: §16.8. 4. **Пак требует «и в каких главах», атрибуции не было.** Внесено: §7.1 — обе формы `开窍大典` в одной главе, не-банковский «свайный дом» через главы 1 и 3. Плюс две правки, внесённые НЕВЕРНО и теперь исправленные: §14.5 объявлял одиннадцать файлов, перечисляя двенадцать при тринадцати на диске (теперь тринадцать и перечень полный), и **запись в `docs/PROGRESS.md` была снимком ДО обоих критиков** — она несла три уже отозванных утверждения («шесть путей», «4 вызова из 33», «слепота из-за склонения первого слова»). Журнал читают РАНЬШЕ отчёта, так что это была худшая из двух. Переписана. ⇒ **Честный итог: круги не сошлись ни на первом проходе, ни на втором, ни на третьем.** Каждый следующий критик находил меньше и мельче (13 → 4 содержательных), но НЕ ноль, и объявлять сходимость я не имею права. Четвёртого прохода не запускала — и это решение, а не забывчивость: находки третьего внесены, документ снова изменился, и следующий проход по изменённому документу снова найдёт опечатки; **граница по существу пройдена там, где перестали находиться ошибки в ЧИСЛАХ и в МЕТОДЕ — третий проход не нашёл ни одной.** ## 18. Фаза 0 и предусловия — исход по пунктам пака Критик полноты заметил, что §4 ссылался на секцию «Фаза 0», которой не было: исход дыма был рассыпан по находкам. Собран здесь. ### 18.1. Чек-лист §4.0 п.2 — что дым обязан был доказать ДО денег | Пункт | Исход | Чем предъявлен | |---|---|---| | стенд поднят и порт отвечает ТВОЕМУ pid | ✅ | `ss -ltnp` показывал `tmplatformd` моим pid на 8097 и `stub` моим pid на 11434 | | гость заведён | ✅ | `POST /auth/dev-login` → 204, строка в `users` | | книга принята | ✅ | `POST /v0/books` → 201, `chapter_count: 3` | | прогон стартовал | ✅ | `POST …/runs` → 202 · ⛔ и **не с первой попытки**: первый старт умер с exit 10 (нет `langpack_root`), второй — с `409 not_priced` до того, как локальной модели дали цену | | кадры доехали | ✅ | `events.jsonl` дымовой книги: 29 кадров шести типов | | узел подписи ответил | ✅ | `awaiting_bank`, exit 3, `.bank.json` с `proposed[]` | | резюм прошёл | ✅ | вторая попытка, exit 0 | | **выгрузка собралась и файл скачался** | ✅ | txt 4522 Б `complete = t`, epub 2984 Б (7 записей zip, `nav`, 3 `itemref`) | | все сборщики следов печатают обещанное | ✅ | `collect.py` прошёл по дымовой книге целиком; позитивный контроль — `checks/poscontrol-collector.txt` | ### 18.2. §4.0 п.4 — «дым меняет три фактора разом» Пак предупреждает, что дым меняет провайдера, книгу и базу сразу, и потому при провале платной руки не разделит причину. **Мера принята и названа в записке-плане: базу из размена я убрала.** Платная фаза шла на ТОЙ ЖЕ базе, ТОМ ЖЕ демоне и ТЕХ ЖЕ трёх бинарях, что дым; сменились ровно два фактора — пайплайн (`pipeline-zero` → `pipeline-c1`) и книга (синтетический дым → срез). Оба названы в §1 с sha. ⚠ Честная оговорка: вместе с пайплайном сменился и `models.yaml` (дымовой нёс номинальную цену локальной модели), и учётка — то есть факторов всё-таки четыре, а не два. Первые два выбраны, вторые два вынуждены находками 1 и денежной правкой оркестратора. ### 18.3. §4.1 п.8 — ключи провайдера **Исход: предусловие закрыто оркестратором, файл НЕ читался ни мной, ни платформой, живость доказана первым платным вызовом.** Путь `TM_PLATFORM_ENGINE_KEYS_PATH=/home/ubuntu-26/projects/textmachine/backend/.env` выдан релеем; платформа не открывает этот файл, а передаёт путь движку аргументом `--keys-file` на `translate`. Дым шёл БЕЗ переменной намеренно — чтобы никакая ошибка конфигурации не могла дотянуться до платного провайдера; контроль этого выбора в логе: два дымовых бута несут WARN «no TM_PLATFORM_ENGINE_KEYS_PATH: the engine is passed no provider keys», платный бут — не несёт, а печатает путь с `source=environment`. Первый платный вызов прошёл без отказа по авторизации (`request_log.err` пуст на всех 33 строках) ⇒ **ключи живы**. ⚠ Паковское «без него платного пути не существует» я **прочитала в коде, но не замерила**: без переменной движок искал бы `.env` рядом с `book.yaml`, которого SaaS-путь не пишет, и отказал бы на проверке ключей при загрузке конфига (`CheckKeys` внутри `forWrite`). Проверять это исполнением значило бы купить заведомо провальный старт — не стала. ### 18.4. §4.2 — что печатается в отчёт без пере-фриза - **Грант:** $2.50 платной учётке, одной строкой `credit_ledger.kind='grant'` (2 500 000 µUSD). - **`TM_PLATFORM_HOLD_FACTOR_PERCENT`:** 125, источник `default` (боевой лог бута); подтверждён арифметикой холда до микро-доллара в §14.6. - **TTL выгрузки:** `TM_PLATFORM_EXPORT_TTL = 24h0m0s`, источник `default`; на обеих строках `exports` `expires_at` ровно на сутки позже `finished_at` (12.09 04:55 и 04:56). ## 19. Состояние сдачи **Сделано и предъявлено:** книга проведена от двери до файла и прочитана; исход есть у каждой из 21 строки пре-рег-таблицы (§6) и у каждого пункта §4 пака (§18 и §12); деньги сведены семью путями с названной оговоркой про независимость (§5) и пере-проверены против вендорской страницы (§16.1); находки размечены «замерено / прочитано / подозрение» (§8, 19 строк); девиации объявлены (§12, шесть пунктов); что НЕ удалось — §10; слабое место прогона — §11; итог трёх проходов критиков — §17 и §17.1. **Что унесено в дерево:** инструменты — `eval/door_to_file/`, закоммичены фризами `be53cc8` и `44b33d9`. Этот отчёт — `docs/experiments/24-door-to-file.md`; его результаты НЕ коммичены, лендит оркестратор. Сырые платные выходы — `books/gu-zhenren/door-to-file/` (33 файла), репозиторий книг отдельный, тоже не коммичены. Запись в журнал — секция «Полигон» `docs/PROGRESS.md`. **Что оставлено включённым и почему:** стенд жив (`tmplatformd` на `127.0.0.1:8097`, база `tm_coldrun_door` в кластере `/tmp:55433`, клон фриза в `~/tm-coldrun-a/freeze`) — чтобы любое число этого отчёта можно было пере-снять теми же командами. Гасить по слову оркестратора, и **только по pid**. **Чего я НЕ делала и не буду:** не чинила ничего из найденного — ни пин цены, ни прибор расхождения форм, ни свой драйвер, ни `langpack_root` в рецепте стенда. Пак запрещает прогону чинить, и это правильно: половина находок — чужих зон, а вторая половина требует замера, а не правки. **Работа завершена, править не планирую.**