textmachine/docs/experiments/24-door-to-file.md

969 lines
116 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Эксперимент 24. Холодный прогон «от двери до ФАЙЛА» — настоящая книга, настоящие деньги
> **Пре-регистрация.** Всё, что ниже строки «ГРАНИЦА ФРИЗА», написано ДО первого платного вызова и
> закоммичено фриз-коммитом. Результаты дописываются ПОСЛЕ прогона, отдельной секцией, и ничего в
> пре-реге не правят: расхождение называется, а не подгоняется.
**Зона:** полигон · **Пак:** `docs/POLYGON_COLD_RUN_A_SESSION_PROMPT.md` (редакция 2) · **Дата:** 11.09.2026
**Предмет:** строка бэклога **16** — цена и проходимость книги, ДОВЕДЁННОЙ ДО КОНЦА, не измерены ни разу.
## 0. Что именно проверяется
Одна настоящая книга (蛊真人, главы 13, 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` |
| Исходник (СРЕЗ, что покупается) | главы 13, 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`; на диске появляется `<book>.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 в главах 13 и «великая
церемония открытия апертуры» ×1 в главе 1 — обе формы сосуществуют в одной главе).
**Механизм РАБОТАЕТ и работает не везде, и теперь у этого есть числа.** Счёт форм, не суждение о
качестве. Разбор — §7.1 и §16.8; цена контура — §5.3.
**Книга дошла до конца.** 蛊真人, главы 13, 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` §катовер п.12: размышление
съедает `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 …
> ⚠ **ИСПР. 17.09 (пометка, не подмена): строка движка ПЕРЕИМЕНОВАНА и по этому тексту больше не грепается.**
> Пак «лестница попытки» (акт `D39.264`, лендинг `10d245a`) вынес петлю в общий контур, и предупреждение зовётся
> теперь `the call was flagged, regenerating with a larger budget` (плюс поле `estimate_usd`). Наблюдение прогона
> верно и не трогается — устарел только способ его найти. Замер: старый токен в `backend/` → 0, новый → 1.
reason=empty next_max_tokens=16992` — движок опознаёт пустой ответ как флаг `empty` и перевыпускает
вызов с удвоенным потолком. Все четыре раза второй вызов вернул текст. То есть путь не рвётся — он
дорожает: **платим дважды и выкидываем первый платёж.**
### 5.3. Терминологический контур оплачен ДВАЖДЫ
Роли банка исполнились до банк-стопа (строки 1016, $0.036554) **и снова после резюма** (строки 2328,
$0.028742). Итого $0.065297 — **15.6 % книги** ушло на контур банка, и **$0.028742 (6.9 %) — это второй
заход того же контура на той же книге**. Заново ли он нужен после движения банка — вопрос к движку;
здесь он просто НАЗВАН и посчитан, потому что раньше эту цену никто не отделял.
### 5.4. Смета против факта — второй замер множителя
| Оценка | $ | множитель к факту |
|---|---|---|
| `expected_usd` движка (нижняя граница по построению: без ретраев и ремонта) | 0.084122 | **×4.99** |
| Ожидание пака, ИСПР. 16 дословно | **0.350.50**, хвост до 0.65 | факт **внутри вилки** |
| Вилка оркестратора по историческому множителю ×3.6 от прогона 08.09 (релей 11.09) | 0.300.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)`
(`<project_db>.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 называет третьим носителем `<StateDir>/runs/<runID>-<attempt>.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» и отказ по
> ⚠ **ИСПР. 17.09:** цитируемая строка переименована паком лестницы (`D39.264`) — искать
> `the call was flagged, regenerating with a larger budget`. Числа и вывод прогона в силе.
`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:2901: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.316.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` в рецепте стенда. Пак запрещает прогону чинить, и это
правильно: половина находок — чужих зон, а вторая половина требует замера, а не правки.
**Работа завершена, править не планирую.**