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

116 KiB
Raw Blame History

Эксперимент 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:truechanged:true; затем preview:falseaccepted[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_usdspend.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() отказывает при <= 0409 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. Что НЕ удалось, не проверено и не измерено

  1. ВЕРНЫЕ ЧИСЛА ПРИ НЕВЕРНОМ МЕТОДЕ — и это худшее, что случилось в моей работе, потому что оно не краснеет нигде. Разбивку попыток я строила по ВРЕМЕНИ (ts против started_at), а ts в sqlite секундной гранулярности: 01:40:45 против 01:40:45.012584 — все шесть подстановок формально «раньше» старта своей же попытки. Числа при этом вышли правильные, по счастливому порядку строк. До этого тот же метод сломался грубее (пробел против T в разделителе дал 33/0), и я его «починила», не заметив, что он негоден в принципе. Настоящий различитель лежал в той же строке и не требует арифметики вовсе: trace_id несёт номер попытки. Нашёл критик чисел; сама бы я не нашла — верный результат не даёт повода перепроверить способ, которым он получен.
  2. epubcheck не гонялся. java на хосте нет, поставить без sudo не пробовала (пак это прямо разрешает — ИСПР. 6). EPUB судится структурно: zip · mimetype · container.xml · nav · 3 itemref · оглавление. Валидность по спецификации EPUB — не измерена.
  3. Эскалация не исполнялась ни разуescalation.budget_usd: 0 в боевом конфиге. Про её работу этот прогон не говорит НИЧЕГО.
  4. Гейт coverage выключен (gates.coverage.enabled: false) — работали четыре гейта из пяти. Судейской стадии на этом пути нет вовсе.
  5. Одна книга, одна пара, три главы, четыре юнита. Множитель «смета → факт» ×4.99 — это ОДНА точка (вторая в проекте). Двух точек мало для закона, и обе сняты на одном исходнике.
  6. Причина расхождения term_consistency_funded на старте не установлена (находка 7): замерено, что ответ POST /runs говорит false, когда строка прогона уже несёт bond_funded = t; ПОЧЕМУ — подозрение (вероятно, ответ рендерится из состояния до допуска), кодом не подтверждено.
  7. Нужен ли второй заход терминологического контура — не выяснено. Замерено, что он случился и сколько стоил; законен ли он после движения банка — вопрос к движку, не к этому прогону.
  8. Прибор расхождения форм видит 40 термов из 69 (§7). «0 расхождений» — утверждение о сорока.
  9. Книжный потолок ($1.25) и стоп-правила ни разу не срабатывали — трата $0.419. Значит их поведение этим прогоном НЕ проверено: они стояли, но не стреляли.
  10. Ручка POST /runs/{id}/stop не трогалась вовсе.
  11. Про качество перевода не сказано ничего и не может быть сказано — 蛊真人 в претрейне.
  12. Дымовая фаза считала НОМИНАЛЬНЫЕ деньги. Локальной модели дана цена (иначе книга не продаётся — находка 2), поэтому суммы дымового аккаунта — это арифметика механизма, а не траты. Платный аккаунт заведён отдельно, и в денежную сверку §5 дымовые строки не входят.
  13. НИ ОДНО 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).
  14. Слабость МОЕГО прибора живости: 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-zeropipeline-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 в рецепте стенда. Пак запрещает прогону чинить, и это правильно: половина находок — чужих зон, а вторая половина требует замера, а не правки.

Работа завершена, править не планирую.