textmachine/docs/BACKEND_COLDRUN_V16_SESSION_PROMPT.md

550 lines
54 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.

# Промт: ХОЛОДНЫЙ ПРОГОН ДВИЖКА на схеме v16 (бэкенд-сессия, ПЛАТНАЯ)
> Выдан оркестратором №21, 31.08. Очередь — п.2 CURRENT-STATE в `docs/PROGRESS.md`.
> **ДЕНЬГИ САНКЦИОНИРОВАНЫ ВЛАДЕЛЬЦЕМ 31.08: ≈$0.60, потолок $0.80, ДЕСЯТЬ глав ПОЛНОЙ ЦЕПЬЮ
> включая редакторскую волну.** Прежняя цифра очереди «≈$0.05» снята как протухшая (§1в).
> ⚠ Промт прошёл два рубежа: механическую сверку 12 блоков и опровергатель в четыре линзы —
> 53 находки применены, 8 из них блокеры. Фактура ниже пере-проверена ИСПОЛНЕНИЕМ; где не смогли —
> сказано прямо.
## 0. Какая проблема и что решит твой результат
Движок переводит книги за деньги. С **27.08** в него легло семь коммитов и ~15 ратифицированных
решений: входная дверь шва, стоп банка ФЛАЖКОМ внутри движка, схема хранилища **v16**, полоса отказов
в два яруса с классом 15, объёмный потолок `--max-units`, гейт схемы, писатель книги (`tmctl build`).
**Ни одно из них не проверено деньгами.**
Проверено исполнением: последний платный вызов движка в корпусе — **17.08** (5 вызовов классификатора
в `coldrun-b/reprobe/classify6`, $0.005582), всё остальное — до 02.08. Ни один существующий леджер не
стоит на сегодняшней схеме, и ни один не гонял ПОЛНУЮ цепь на HEAD.
Значит все эти решения сегодня — обещания кода и тестов, а не факты о работающей системе.
**Твой результат** — первый платный сквозной прогон на текущем HEAD, который отвечает на один вопрос:
**переводит ли движок книгу насквозь и отдаёт ли файл — на настоящих деньгах, настоящих ключах,
настоящем провайдере.** Не «зелёная батарея», а книга, которую можно открыть.
Он же снимает гейт с чужих работ, ждущих его с 20.08: строки бэклога **202** (живой прогон через API
платформы) и **216** (подпись банка · halt на потолке · резюм после пополнения против ЖИВОГО движка).
Слово владельца 20.08: «гонять одну неготовую систему против другой смысла нет» — поэтому первым идёт
движок, один. Попутно и почти даром закрываются строки **154**, **157**, **160**, **198** (§5.7).
**Качество перевода — НЕ твой предмет.** Художественную оценку делает полигон. Ты меряешь МЕХАНИКУ и
ДЕНЬГИ. Границы предмета: ты не судишь, хорош ли перевод, не калибруешь ставку платформы и не трогаешь
дверь выдачи (её в платформе просто нет — `createExport`/`getExport` не смонтированы).
## 1. Что именно ты делаешь
### 1а. Форма прогона — делай РОВНО так
**Новый проект, новый каталог, новый `book_id`.** Ни один существующий стенд не переиспользуется.
Причины механические, все проверены исполнением:
1. **Схема.** Проектные БД корпуса стоят на **v7v15** (на v14 — 13 из 36, одна на v15), бинарь HEAD —
на **v16**; число применяемых версий у каждого стенда своё. Мигрировать чужой стенд ради холодного
прогона — значит необратимо тронуть чужую улику.
2. **Конфиг.** Старый `book.yaml` с ключами `mined_delta:`/`mined_rejects:` **ОТКАЗЫВАЕТСЯ, exit 10**
(D39.158 п.5): пути решений теперь выводятся конвенцией `<каталог>/<book_id>.mined-delta.yaml`.
Такие ключи несут 17 файлов корпуса, включая оба прежних холодных прогона.
3. **Потолки.** Потолки существующих стендов пробиты новой ценой и защищать перестали (`coldrun-a`
`book_usd: 0.25` против пересчитанных ≈$0.56).
**Каталог:** `books/gu-zhenren/coldrun-v16/` — создаёшь ты.
**Исходник:** `books/gu-zhenren/coldrun-a/guzhenren-ch1-10.gb18030.txt`
(sha256 `0b5f9b0266d8…`, 28 174 знака без переводов строк, 10 глав). Копируешь к себе, оригинал не
трогаешь. Тождество байтов выбрано НАРОЧНО: **ровно этот файл лежит и под `bank-low`, и под `minirun`**,
то есть под обоими историческими базлайнами, с которыми ты сравниваешь деньги (§5.3).
**`pipeline.yaml` — копия `coldrun-b/reprobe/bank-low/pipeline.yaml`** с починенными по §1б путями.
Любое отличие выпиши в записку-план ПОСТРОЧНО: сравнимость денег держится на конфиге, а не только на
байтах исходника. **`escalation.budget_usd` обязан быть > 0** — в репозиторном шаблоне он 0, и тогда
«эскалационные хопы» §5.3 и запас $0.60→$0.80 меряют пустоту. **Сид: идём БЕЗ `glossary_seed`**, как
bank-low; решишь иначе — это пинг, а не выбор.
### 1б. ⚠ ЛОВУШКА ПУТЕЙ — главная в этом паке, проверь ПЕРВЫМ делом
На машине **ДВА чекаута** одного репозитория:
| Путь | Ветка | Что там |
|---|---|---|
| `/home/ubuntu/projects/textmachine-main` | `main` | ТВОЙ. HEAD с паком писателя книги |
| `/home/ubuntu/projects/textmachine` | **`polygon`** | ЧУЖОЙ, ветка полигона |
`books/` в твоём дереве — **СИМЛИНК** на `/home/ubuntu/projects/textmachine/books`; `/home/ubuntu/books`
**тот же симлинк**. То есть корпус физически лежит в чужом чекауте и версионируется ОТДЕЛЬНЫМ git-репо.
**`book.yaml` несёт СЕМЬ абсолютных путей под ДВУМЯ разными корнями.**
- Под `/home/ubuntu/projects/textmachine/` — только **`models:`** и **`langpack_root:`**.
**ОБА переведи в `/home/ubuntu/projects/textmachine-main/backend/configs/…`.**
- Под `/home/ubuntu/books/``pipeline:`, `source_file:`, `project_db:`, `langpack_extend:`.
Это твой каталог прогона; они и должны смотреть туда.
- `prompt_override:` — если используешь, это `…-main/backend/prompts/`, НЕ `configs/`.
**ЕДИНСТВЕННОЕ ИСКЛЮЧЕНИЕ — `mining.contrast_path`.** Jieba-контраст лежит ТОЛЬКО в
`/home/ubuntu/projects/textmachine/eval/exp16/data/jieba_dict_general_zh.txt` (5 МБ, gitignored); в
`textmachine-main` его нет, а репозиторный `pipeline-c1.yaml` секции `mining:` не несёт вовсе. Оставь
путь в polygon-дереве как есть (чтение чужого чекаута законно) либо скопируй файл к себе. **Без него
`--verify-bank` отказывает, и ось §5.4 закрывается целиком.**
**Зачем всё это.** В polygon-дереве **нет каталога `langpacks/ru`** — он есть только в `main`. Два
чекаута `backend` различаются ТОЛЬКО этим: `models.yaml` и промпты совпадают побайтово, так что снапшот
прогона от выбора чекаута не зависит. Но `ru/reader.txt` — это слова, которые читатель видит на дыре.
`LoadReaderWords` на отсутствующий каталог возвращает дефолты **без ошибки**.
**Проверки исполнением, обе обязательны:**
1. `ls -d <твой langpack_root>/ru/reader.txt` — ДО покупки.
2. Ось закрывается только вторым шагом из §5.1 (`--partial` на книге с дырой). ⚠ **На complete-книге
ловушка НЕВИДИМА:** сборка с `langpacks/ru` и без него даёт побайтно один файл и ни одного WARN.
⚠ Дисциплина симлинка: `find books -name X` **без `-L` даёт ЛОЖНЫЙ НОЛЬ** (оркестратор на этом уже
попался). Всегда `find -L`. И никогда не создавай симлинков внутрь `books/`.
### 1в. Деньги — санкция владельца, делай РОВНО так
Очередь несла «≈$0.05». **Цифра устарела в ~4.5 раза:** DeepSeek пере-пинен коммитом **15.08**
(`76049bb`, D39.137) под вендорские цены, вступившие в силу **16.08 16:00 UTC** — flash
`input 0.14→0.44`, `output 0.28→1.32`; pro `0.435→1.32` / `0.87→3.96`. Заход `bank-low`, стоивший
$0.045095 в июльских деньгах, по этим ценам стоит **$0.2017** (множитель 4.47, пере-считано из его же
токенов).
**Слово владельца 31.08: ≈$0.60, потолок $0.80, ДЕСЯТЬ глав, ПОЛНАЯ ЦЕПЬ включая редакторскую волну.**
**«Потолок» здесь не абсолютный.** Он сверяется ДО каждого вызова против `committed + reserved`, но
против ОЦЕНКИ, а списывается факт — поэтому итог может выйти за $0.80 на величину порядка одной
максимальной резервации (`backend/internal/store/ledger.go` — «do not overshoot the ceiling by more
than one maximum reservation»). На этом объёме это центы; знать об этом обязана ты, а не отчёт.
Обязательны все три ручки:
- **`--ceiling-usd 0.80`** на `translate` — КНИЖНЫЙ КУМУЛЯТИВНЫЙ потолок, перекрывает `book.yaml`,
назад не пишется, обязан быть > 0;
- **`ceilings: { book_usd: 0.80, day_usd: 1.00 }`** в `book.yaml` — продублировать ОБА (Р7: конфиг
падает, если оба ≤ 0; день не должен срабатывать раньше книги);
- **`--max-units` — ДВУМЯ ПОРЦИЯМИ, и это несущее требование, а не оптимизация.**
- **Первая покупка:** `--max-units` заведомо малый (23 юнита). Обязана дать **exit 0** и строку
«Next: N output unit(s) … have never been delivered». Это **единственное живое предъявление
объёмного потолка** — механизма, который §0 называет среди проверяемых. При `--max-units` =
`units_total` он не сработает НИ РАЗУ и останется там же, где был: в тестах.
- **Вторая покупка:** остаток, `--max-units` = `units_total` из `tmctl manifest --json`. Уже
оплаченные юниты не считаются и не пере-оплачиваются. Сохрани WARN про майнящую банк книгу и
ЗАФИКСИРУЙ, потребовался ли `--resnapshot` — это живое основание `PD-422` и п.5 очереди.
**$0.60 — ОЖИДАНИЕ, $0.80 — потолок. Между ними запас на ретраи и эскалационные хопы**, которые
объёмный потолок не тратит, а деньги тратят. **Упёрся в $0.80 — это exit 4, СТОП и ПИНГ, а не тихая
пере-нарезка скоупа.** Подъём потолка просят при СРАБАТЫВАНИИ стопа, не по проекции: прогнозный запрос
оставляет ложный след «владелец согласился на пере-оплату», которой не было.
**Сметы ПЕРВОЙ покупки у движка НЕТ**`--dry-run` у `translate` отвергается явно. Потолки и есть
твоя единственная защита. Смета ПЕРЕ-прохода существует, она $0 и без ключей.
**Гони в ДОЛИНУ.** У DeepSeek цена ходит окнами: пики **UTC 0104 и 0610 вдвое дороже**. Цены в
`models.yaml` пинованы ПИКОВЫЕ, поэтому в долине леджер завысит — это в безопасную сторону. **Время
старта запиши ПОЛЕМ в артефакт:** перенос машины обнулил mtime всего сырья, на mtime не опирается никто.
### 1г. Состав прогона
Гони **полную цепь, включая редакторскую волну, на ДЕСЯТИ главах** — объём и состав заданы словом
владельца (§1в), тобой не выбираются.
Довод, почему не draft-only, и он честный: проверено исполнением по всем **36** базам корпуса — роль
`judge` не вызывалась **НИ РАЗУ нигде**, а редакторская волна **гонялась и оплачена** на малых
прогонах (19 баз), но **НЕ на холодном старте**: у `coldrun-a` и во всей семье `coldrun-b` ноль
редакторских вызовов. Именно это держит строку бэклога **16**. Не измерена не «редактура вообще», а
ПОЛНАЯ ЦЕПЬ ОДНИМ ЗАХОДОМ на холодном старте: draft+терминолог и edit меряли разные стенды под разными
конфигами.
Десять глав выбраны ради сравнимости: тот же байтовый исходник и тот же объём, что у обоих исторических
базлайнов, поэтому множитель «июль → сегодня» получается без экстраполяции (§5.3).
⚠ Приор оркестратора, **опровергается твоим замером**: draft+терминолог ≈$0.20, редакторская волна на
тех же десяти главах ≈$0.380.40, итого ≈$0.58 при потолке $0.80. Не влезает — **стоп и пинг, не режь
скоуп молча**.
## 2. Зона записи и git
**Пишешь:** `books/gu-zhenren/coldrun-v16/` (каталог создаёшь) + отчёт сессии.
**Не пишешь:** `backend/`, `platform/`, `frontend/`, `eval/`. **`docs/` — тоже не пишешь**, и это
ОСОЗНАННОЕ сужение канона на этот пак: `CLAUDE.md` разрешает бэкенд-сессии писать итог в свою секцию
`docs/PROGRESS.md`, но здесь итог и пинги едут ОТЧЁТОМ, а в `PROGRESS.md` и `archive/reports/` их кладёт
оркестратор — иначе твоя строка уедет чужим коммитом из живого дерева. Пинг = секция отчёта либо
прямой канал §12.
- **Ты НЕ коммитишь.** Ни в этот репозиторий, ни в репозиторий книг. Лендит оркестратор.
-**Книга и производные в репозиторий `textmachine` не коммитятся НИКОГДА** (CLAUDE.md).
-**НИКОГДА:** `git add -A` · `git add .` · `git commit -a` · `reset --hard` · `checkout` поверх
грязного дерева · перезапись истории · `git stash` (стек общий с другими сессиями).
- В дереве лежит незакоммиченная работа ДРУГИХ живых сессий (`platform/**`) — **не трогать вообще**.
**Прогон НЕИЗБЕЖНО пачкает репозиторий КНИГ, и роли для него в проекте не определено.** Тот
репозиторий ОТСЛЕЖИВАЕТ `.db``-wal`/`-shm`), а `.book.epub`/`.book.txt` его `.gitignore` не
покрывает; `tmctl migrate` переписывает отслеживаемый файл БД. В ролевой таблице `CLAUDE.md` зоны
`books` **нет вообще**. ⇒ **Ничего там не коммить и не прибирать.** Оставь `git -C books status`
грязным, назови его состояние в отчёте дословно, вопрос «кто коммитит книги» подними пингом.
**И ЧИТАТЬ чужие БД не безобидно.** `sqlite3 <db> …` **без `?mode=ro`** открывает базу НА ЗАПИСЬ,
чекпойнтит WAL и УДАЛЯЕТ отслеживаемые `-wal`/`-shm`. Проверено исполнением при подготовке пака: одна
SELECT-выборка по `coldrun-a` оставила две строки ` D` в чистом до того репозитории. ⇒ **Любое чтение
чужой базы — только `sqlite3 'file:<путь>?mode=ro'`.**
**Нашёл баг движка — ПИШИ, не чини.** Правка `backend/` — отдельный пак. Исключение ровно одно: если
без однострочной правки прогон физически не стартует, ты **пингуешь** и ждёшь, а не патчишь молча.
## 3. Карта чтения — ЗАКОН, пять позиций
1. **`docs/experiments/00-provider-quirks.md`** — ЦЕЛИКОМ и ДО первого платного вызова. Обязательное
пре-чтение перед любым обращением к провайдеру: живые слаги, thinking, `finish_reason`,
игнорируемые параметры. ⚠ Проба падает или ведёт себя странно ⇒ **идём в вендор-доку, НЕ гадаем**.
2. **`docs/architecture/18-bank-ontology.md`** — ратифицирована D39.158, обязательное пре-чтение перед
всем, что трогает банк. Три роли носителей · единственные писатели · дисциплина проекции.
3. **`backend/README.md`** — механика движка: волны, стадии, чанкование, артефакты.
4. **`docs/architecture/15-money-path.md` §2 «Деньги ДВИЖКА»** — леджер, оба потолка, leftover-reserved.
⚠ Якоря дока пере-проверены и починены 31.08, им можно верить.
5. **`docs/architecture/17-seam-inbound-law.md`** — закон входной двери шва.
Всё остальное — код первичен, достаёшь сама грепом. Ратифицированное вложено в тело этого промта.
## 4. Разметка свободы
**Делай РОВНО так (отступление = пинг оркестратору, не интерпретация):**
- новый каталог, новый `book_id`, `pipeline.yaml` — копия bank-low с `escalation.budget_usd > 0`,
без сида; чужие стенды не мигрировать и не переиспользовать (§1а);
- пути: `models:` и `langpack_root:` — в `textmachine-main`; `mining.contrast_path` — исключение (§1б);
- **число глав ДЕСЯТЬ и `--max-units` двумя порциями — санкция владельца (§1в), не твой выбор.**
⚠ Меньшее N на ВТОРОЙ покупке даёт остановку по объёму, а она печатает **exit 0** — ШТАТНОЕ
ЗАВЕРШЕНИЕ, неотличимое в коде от «книга переведена». Скоуп молча усохнет;
- бинарь `tmctl` **собрать из HEAD** своего дерева и записать `sha256`. Причина не в том, что стендовый
`~/.local/bin/tmctl` чего-то не умеет — он от 30.08 23:17 и пак писателя книги в нём есть, а на 31.08
он побайтно совпадает с HEAD-сборкой. Причина в том, что **личность бинаря, которым куплен прогон,
обязана быть зафиксирована числом**, а стенд пере-собирают чужие сессии без предупреждения;
- ключи — `--keys-file` **абсолютным** путём. Цепь резолва: окружение → `--keys-file``.env` рядом с
`book.yaml``./.env` в CWD. `--keys-file` **падает громко** на нечитаемом или пустом файле и
принимается **только `translate`**. Единственные заполненные файлы ключей на машине —
`/home/ubuntu/projects/textmachine/backend/.env` и `.../eval/.env` (576 Б, оба тронуты 30.08 01:01,
что совпадает со словом владельца «починил ключи 30.08»). ⛔ **Файлы ключей НЕ ОТКРЫВАТЬ** — ни тебе,
ни субагентам. Наполненность проверяется ПРОГОНОМ;
- оба денежных потолка выставлены до первого платного вызова (§1в);
- деньги считаются **из сырого леджера ДВУМЯ независимыми путями** (§6.3);
- **`make battery` в `backend/` ДО первой покупки.** Не `go test ./...` — батарея держит ещё
`vet -tags live`, `gofmt`, `golangci-lint` и `-race`, и только она гейт зоны. Скипы вслух;
- **`tmctl migrate` отдельным шагом** — что он применит и что печатает, см. §1а п.1 и §6.2;
-**ПЕРВАЯ ЖЕ правка исходника закрывает платную часть НАВСЕГДА** — правило §5.6, и оно бьёт твою
свободу выбирать порядок фаз.
**Решаешь сама и аргументируешь:**
- порядок фаз внутри платной части и где ставить свои стоп-точки;
- нужен ли `--verify-bank` и в какой фазе (см. §5.4);
- какие дополнительные $0-пробы дёшевы и стоят того.
## 5. Что прогон обязан предъявить — по осям
### 5.1 Сквозной путь, структура глав и файл книги
`translate``status``report``manifest --json``export --json --pairs` (и отдельно голый
`export` — это РАЗНЫЕ каналы) → **`build`**.
**Шесть состояний честности D39.175 — это pending · withheld · incomplete · ghost · stale · drift, и
живут они в ТРЁХ разных местах**, отчего и разъезд счёта «шесть состояний, но пять дыр». Четыре —
перечисление `HoleKind` (`HolePending` · `HoleWithheld` · `HoleIncomplete` · `HoleStale`; пятая
константа `HoleNone` = дыры нет). Пятое, **ghost**, — не `HoleKind`, а счётчик `BookExport.GhostRows`:
переведённый текст, которому текущая нарезка не находит места; целостность он рубит наравне с дырами
(`complete := len(holes) == 0 && exp.GhostRows == 0`). Шестое, **drift**, — булево поле отчёта и WARN,
в файл не пишется.
**Твой прогон штатно даст ОДНО:** `complete=true` либо `pending`. Остальные — $0-провокации ПОСЛЕ
платной части, и порядок у них жёсткий: **§5.6**.
**`config_drift=false` и `stale_unknown=false` — самая дешёвая НОВАЯ фактура прогона.** Оба поля ни
разу не имели честного «нет»: все стендовые книги под дрейфом. **`stale_unknown=true` на свежей книге
означает СБОЙ, а не дрейф** — ищи его в WARN. ⇒ **Сохраняй stderr прогона целиком.**
**Структура глав — узел строки бэклога 160, и он $0.** По `manifest --json` назови: сколько глав
получили `heading` из текста, а сколько — цифровой рендер движка; совпадает ли число глав с 10; что
напечатано меткой главы в собранных EPUB и txt. На листе владельца висит ровно этот вопрос (В-4/Ф-30,
лейбл главы без заголовка) — приложи **дословные метки**, а не пересказ.
**Две ловушки `build`:**
1. `build --format epub` **МОЛЧА УДАЛЯЕТ** лежащий рядом `.book.txt` (и наоборот): набор рядом с БД
всегда от ОДНОЙ сборки. А `StatusArtifacts.book_files` публикует ПОЛНУЮ карту форматов независимо от
того, что записано. ⇒ **Собирай ОБА формата одной командой** (без `--format`) и не считай наличие
пути в `book_files` доказательством наличия файла.
2. После коммита файлов `build` удаляет копии незапрошенных форматов, и сбой этого `os.Remove`
возвращается ГОЛОЙ ошибкой ⇒ **exit 1 при том, что новые файлы УЖЕ на диске, а `BuildReport` не
печатается вовсе**. ⇒ **При ЛЮБОМ ненулевом коде посмотри, что реально лежит на диске.**
**ПОРЯДОК СБОРКИ.** Чистая книга собирается и СОХРАНЯЕТСЯ **до** любой правки исходника: собери оба
формата одной командой, скопируй пару файлов в `artifacts/clean/` внутри каталога прогона — и только
потом провоцируй. Демонстрацию `--partial` гони с `--out` в отдельный путь, иначе она перезапишет НА
МЕСТЕ ровно тот файл, который ты отдаёшь владельцу.
**Ось «издательский результат».** Клейм очереди «0 fatals / 0 errors на трёх стендовых EPUB» был «со
слов сессии»; **две трети пере-проверены независимо** при подготовке промта: `epubcheck` на машине НЕТ,
но java 21 есть и валидатор приносится одной командой; пере-собранный бинарём HEAD EPUB настоящего
перевода (стенд `minirun`) дал 0 fatals / 0 errors. Третья точка — **твой** EPUB.
⚠ Третий стенд (`acceptance`, единственный с `ghost`/`withheld`/`incomplete`) дёшево не пере-собирается
его pipeline-конфиг протух по трём классам. Эти три состояния либо возникнут у тебя сами, либо
**останутся непроверенными, и так и запиши**.
**Живой читалки на машине нет ни одной.** Не изобретай: **передай файл владельцу** и назови ось
открытой до его ответа.
### 5.2 Коды выхода — таблица, сверяй фактические
| Код | Что случилось |
|---|---|
| 0 | чисто (в т.ч. остановка по `--max-units` — это ЗАВЕРШЕНИЕ) |
| 1 | инфраструктура, ошибка разбора флага, пойманная ПАНИКА |
| 2 | завершено С ФЛАГАМИ |
| 3 | стоп подписи банка (намеренная человеко-в-цикле пауза, НЕ падение) |
| 4 | halt по денежному потолку (книга или день) |
| 5 | graceful stop (пойман SIGINT/SIGTERM) |
| 10 | конфиг не поедет: нечитаем, неразбираем, невалиден, нет ключа |
| 11 | исходник КНИГИ не читается/не декодируется |
| 12 | проект залочен другим `tmctl` |
| 13 | схема проекта не этого бинаря — нужен `tmctl migrate` |
| 14 | документ решений прочитан и отклонён — решает ПОЛЬЗОВАТЕЛЬ |
| 15 | документ принят, запись не завершилась — отчёт называет, какой файл лёг; шли тот же документ снова |
| 16 | `build` отказался писать книгу с дырами — либо доделать, либо `--partial` |
| 19 | класс отказа, номера которому этот бинарь не знает |
Каждый код, который ты увидишь, — с командой и фактическим `$?`. Кода, которого не видела, не называй.
**Exit 15 у `build` на этом прогоне НЕДОСТИЖИМ** без инъекции сбоя ФС между двумя `rename` — не
заказываю. Но знай: 15 означает, что ЧАСТЬ файлов уже на диске.
### 5.3 Деньги
Разложи стоимость по стадиям и ролям и положи на **ОБА** исторических базлайна — они меряли разное, и
только вместе покрывают твою цепь:
- **`bank-low`** (`coldrun-b/reprobe/bank-low/guzhenren-bank-low.db`) — терминологический контур:
draft flash $0.021289 + draft pro $0.010350 = $0.031639 · терминолог $0.005750 · классификатор
$0.007706 · **весь заход $0.045095**;
- **`minirun`** (`books/gu-zhenren/minirun/guzhenren-minirun.db`) — РЕДАКТОРСКИЙ контур на том же
исходнике побайтно: draft flash $0.018834 (58) · draft pro $0.003371 (3) · **edit editor pro
$0.092086 (16)** · весь заход $0.114291.
Оба — в ИЮЛЬСКИХ деньгах. Твоя задача — дать то же разложение в СЕГОДНЯШНИХ и назвать множитель.
**Леджер = НИЖНЯЯ граница** (строка 78). «Оплаченный хвост» собирается из **ДВУХ таблиц**:
- по `checkpoints` — ретраи (`attempt>0`) и эскалационные хопы (`escalation=1`). ⚠ У `checkpoints`
**НЕТ `book_id`** — связь только через `JOIN jobs j ON c.job_id = j.id WHERE j.book_id=…`;
- по `request_log``ok=0 AND cost_usd>0` и `estimated=1`. ⚠ Колонок `attempt`/`escalation` в
`request_log` НЕТ, запрос по букве старой редакции упал бы.
**НЕ путь к сумме:** `chapters[].cost_usd` из `status --json` — это стоимость только ПОСЛЕДНЕЙ
резолюции строки.
**В денежных таблицах НЕТ идентификатора прогона:** `checkpoints` и `spend` пожизненные по книге,
единственная ось разделения прогонов в них — `checkpoints.created_at` (UTC). Ось прогона есть только в
телеметрии (`request_log.trace_id`). **Ты покупаешь ДВУМЯ порциями (§1в) — спланируй разбор заранее.**
### 5.4 Банк и стоп подписи
Стоп банка стал **флажком на НОВИЗНЕ** (D39.158 п.1): `--verify-bank` останавливает прогон перед
edit-волной **только** если в карте есть кластер, вся поверхность которого ещё не предъявлялась; память
предъявления — `bank_stop_presented`, схема v16. Прогон, который НЕ стопится, обязан свернуть
неподписанные строки в банк ПЕРЕД стадией.
**Нота проверяется ДВУМЯ прогонами — реши сама, влезает ли.** Первая половина: первый `--verify-bank`
даёт РОВНО ОДИН стоп. Вторая: повторный `--verify-bank` **БЕЗ единого решения по термам НЕ стопится
второй раз** — это и есть новизна-как-флажок. Проверив только первую, ты проверишь ноту наполовину, и
так и напиши. Наблюдаемое: `exit 3` · строка «the delta holds terms no stop has shown before» · строки
в `bank_stop_presented` · файл `<db>.mined-signature.yaml`. Провал записи памяти НЕ валит прогон — он
даёт один лишний доброкачественный стоп, и это штатно.
Форматы, изменившиеся и ни разу не виденные на живых данных: `<db>.mined-signature.yaml` получила
конверт `map_version: tm-signature-map-v1` + `map_id`; `<db>.bank.json` — якорь свежести `as_of`.
**Атомарность сайдкаров пере-проверена по коду 31.08:** через `writeFileAtomic` идут **ЧЕТЫРЕ**
`<db>.manifest.json`, `<db>.bank.json`, `<db>.mined-signature.yaml`, `<db>.auto-bank.yaml`; неатомерен
**ОДИН**`<db>.bank-stop.txt` (`os.WriteFile` с усечением; это отрендеренная ТАБЛИЦА, не JSON).
**Файла `bank-stop.json` не существует.** Форматы книги идут через staged-commit. ⚠ Прежняя запись №18
(«атомарны три из шести, среди них `bank-stop.json`») в этой части УСТАРЕЛА — если встретишь её в
доках, это она, а не твой результат.
**Строка бэклога 240** («оплаченный стоп подписи может исчезнуть МОЛЧА») заведена оркестратором №20
ЧТЕНИЕМ КОДА и честно помечена **PLAUSIBLE — сценарий не воспроизведён**. Её платформенную половину
берёт пак P12. Дёшево предъявишь что-то ЗА или ПРОТИВ — это ценнее чтения кода; дорого — не гонись,
скажи в obstacles.
### 5.5 Что ты оставляешь платформе (это и есть снятие гейта 202/216)
Платформа **сочинила сама** форму артефактов шва и никогда не видела настоящих. Каналов **ПЯТЬ**, а не
три: **манифест** (`manifest --json`) · **сайдкар банка** (`<db>.bank.json`) · **причины флагов** ·
**`events.jsonl`** (аутбокс схемы v15 — главный канал шва, который твой прогон и создаёт) ·
**`export --json --pairs`** (единственный канал, несущий ТЕКСТ).
**Чего ждёт другая сторона — читай в ЕЁ коде, не по памяти и не из этого промта** (D39.132): манифест —
`platform/internal/ingest/manifest.go`, сайдкар банка — `platform/internal/ingest/bank.go`, причины
флагов — `platform/internal/ingest/notes.go` (рукописный словарь причин → коды замечаний контракта).
Читать чужую зону можно, писать в неё нельзя.
**Карта причин флагов — НЕ файл движка:** движок отдаёт причины значениями `flag_reason` в
`export`/`status`; это и есть строка бэклога 204. Сравнивай наблюдённые значения со словарём
`notes.go`. ⚠ **Расхождений может не быть вовсе** — «сверено, расхождений ноль, вот обе стороны
списком» полноценный результат, а не пустая работа.
Каталог прогона остаётся на диске целиком; в отчёт положи ФОРМУ артефактов (структура, ключи, примеры
значений) — отчёт уедет в репозиторий, каталог нет.
**Дёшево и сверх заказа, если останется время** (обе пробы $0, обе после платной части): назови в
отчёте путь к своей БД и её размерности (глав/юнитов/строк) — `PD-297` платформы ждёт корпусную книгу
для замера round-trip, и без этого следующая сессия пере-покупает прогон.
### 5.6 $0-провокации ПОСЛЕ платной части — ЖЁСТКИЙ ПОРЯДОК
⛔ **ГЛАВНОЕ ПРАВИЛО, и оно бьёт твою свободу выбирать порядок фаз (§4): ПЕРВАЯ ЖЕ правка исходника
закрывает платную часть НАВСЕГДА.** После неё ни `translate`, ни `redrive` по этой книге не
запускаются. Причина — строка бэклога **238**, подтверждённая обеими половинами: резюм-фаст-пас
требует совпадения И `content_hash`, И снапшота; правка исходника НА МЕСТЕ (исходник в снапшот не
входит) роняет фаст-пас, и `translate` **перекупает юниты БЕЗ предупреждения в смете**. Гейт
`--accept-rebill` этот путь не ловит по конструкции. Единственный видимый след — INFO-строка
`manifest: the stored manifest is stale (source or cut changed)`.
Порядок проб — ровно такой:
1. **`stale`** — правка исходника, **РАВНАЯ ПО БАЙТАМ** и внутри чанка (сдвинешь границы нарезки —
получишь `ghost` вместо `stale`, а в отчёте напишешь, что stale проверен) → `status --json`
`build`. ⛔ Идёт ПЕРВОЙ: `staleUnits` под config drift возвращает «неизвестно» сразу, ничего не
считая, а любая правка конфига двигает снапшот ⇒ `drift=true` ⇒ ось `stale` для этой книги закрыта
НАВСЕГДА.
2. **Строка 238** — на тех же данных: `status --json` скажет, что пере-оплаты нет, а `build` скажет
`stale N`. **Расхождение двух $0-поверхностей и есть предъявление строки.**
3. **`--partial` / дыры** — с `--out` в отдельный путь (§5.1), и здесь же глазами найти русскую строку
маркера дыры — это второй шаг оси §1б.
4. **Второй `--verify-bank`** (§5.4).
5. **Второй `translate`** — строка бэклога **198**: юниты резолвятся бесплатно; посмотри, появились ли
ПОВТОРНЫЕ анонсы юнитов в `events.jsonl`. Ключ анонса — `unit:<bookID>:<wave>:<chapter>:<unit>`, без
метки нарезки; это первый настоящий аутбокс на схеме v15 и первая возможность увидеть механизм на
данных, а не в коде.
6. **ПОСЛЕДНЕЙ — строка 239:** снять edit-стадию в **КОПИИ** `pipeline.yaml``status --json` покажет
`config_drift=false` и черновики как полную книгу, а `export`/`build` на том же конфиге покажут
дрейф. ⛔ Эта проба двигает снапшот, поэтому она последняя.
### 5.7 Что закрывается ДАРОМ — из того, что ты и так сохраняешь
**Строки бэклога 154 и 157** гейчены именно этим прогоном и закрываются числами, которые уже печатаются:
из stderr терминолога выпиши `families` / `families_refused` / `families_held`; из `manifest --json`
распределение размеров юнитов (минимум/медиана/максимум знаков) и `units_total` по главам. Это
единственный носитель замера, на который обе строки гейчены; без него они переезжают на следующий
платный прогон.
## 6. Мандат самопроверки ИСПОЛНЕНИЕМ
Самоотчёт «проверено» без исполнения регулярно оказывается ложным и здесь стоит ДЕНЕГ.
**Тебе ЯВНО разрешены субагенты.** Модель задавай ИМЕНЕМ и знай, сколько их у тебя работает.
По умолчанию для трудного суждения — **Fable 5**; обычно хватает одного-двух, веер под предмет
выбираешь ты.
1. **Записка-план ДО первой покупки** — что покупаешь, каким потолком защищено, чем докажешь; `N` для
обеих порций `--max-units` названы числом. Пишется в каталог прогона **и ДОСЛОВНО повторяется первой
секцией отчёта**: отчёт уезжает в репозиторий, каталог прогона нет. Записка, дожившая только до
конца сессии, приёмке не предъявляется.
2. **Сухой проход $0 ПЕРЕД покупкой:** `status` · `manifest` · `report` ключей не требуют. ⚠ Но
`status`/`manifest` на никогда-не-запускавшейся книге **СОЗДАЮТ и мигрируют БД**. ⚠ На ТВОЕЙ свежей
книге `tmctl migrate` напечатает `migrate: created <db> at schema v16` (или `already at schema v16`);
**`backups/` НЕ появится** — restore point на новой БД не берётся. Строка `pre-migrate: backed up …
at schema v14` — контракт СУЩЕСТВУЮЩЕЙ книги, и её ты не увидишь. Хочешь увидеть глазами — сделай на
СВОЕЙ КОПИИ чужого стенда вне `books/`, и так и запиши.
3. **Деньги — двумя независимыми путями.** Оба печатает ОДНА $0-команда
`tmctl report --config book.yaml`: строка `Book ledger: committed=… reserved=…` считает
`SUM(spend.committed_usd)`, а `ROUTING/MONEY: … spend by model:` собирается из `checkpoints`.
Сойдутся — приводишь обе; разойдутся — это НАХОДКА, а не помеха.
4. **Активная проверка живости.** Раз в пару часов сверяй рост числа вызовов и трат по леджеру ДВУМЯ
чтениями. Зависшая задача от идущей неотличима по молчанию.
5. **Адверсариальный проход по СВОЕЙ ГОТОВОЙ работе — ПЕРЕД сдачей.** Не перечтение: субагент получает
твой отчёт, твои команды и мандат «найди, где она выдаёт непроверенное за проверенное».
**Артефакт — секция отчёта «Адверсариальный проход»:** какой моделью, сколько агентов, что каждому
дано на вход, и КАЖДАЯ находка дословно + твоя диспозиция (принято и исправлено / отклонено с
доводом). Ноль находок — тоже запись, но тогда назови, что именно субагент проверял и какими
командами.
Оси, на которых пак уязвим **по моему суждению** — приор, не потолок: клеймы о деньгах · клеймы о
кодах выхода, которых ты не видела · клейм «EPUB валиден» · утверждение, что артефакт совпал с
ожиданием платформы. Считаешь, что уязвимо другое (тождество нарезки, честность `complete`) —
замени ось с доводом и скажи об этом.
6. **Перед отчётом сверь КАЖДЫЙ клейм с результатом инструмента ЭТОЙ сессии.**
7. **Последний абзац отчёта — план или обещание? Сделай сейчас.**
8. **Интервальная сверка с заказом.** На каждой естественной границе (после сухого $0-прохода · после
первой порции · после edit-волны) прогоняй по осям §5 короткую сверку «что уже предъявлено / что ещё
нет», и хотя бы одну отдай субагенту с явными критериями §7. Длинная сессия дрейфует от заказа
незаметно для себя.
**Тесты и гейты не подгонять под зелень.** Править или удалять тест/голден/гейт, чтобы он прошёл, —
НЕДОПУСТИМО. Несогласие с тестом — пинг, не правка.
## 7. Записка-план и комплектность
До первой покупки — записка-план (§6.1). Перед сдачей — механическая сверка отчёта против §5 по осям:
каждая ось получает вердикт **предъявлено / не предъявлено / опровергнуто**. Три исхода, не два:
«не проверено» ≠ «опровергнуто».
## 8. «Заявление = команда»
Каждое число и каждая категорика отчёта идут С КОМАНДОЙ, которой получены. Приёмка пере-ранит
выборочно. Клейм о рантайме без живого прогона = **PLAUSIBLE**, и так и помечается. Клейм без
ре-рана — «со слов сессии».
## 9. Эхо-протокол старта
**ДО первой правки и ДО первой покупки** — ≤10 строк: скоуп / инварианты / чего не делаешь.
Отправь первым действием по каналу (§12). Эхо служит и проверкой канала.
## 10. Obstacle reporting
Обязательная секция отчёта: **что НЕ удалось и что НЕ проверено**. Отсутствие инструмента, недостижимая
ось, непонятое поведение — сюда. Пустая секция при непустой работе читается как дефект отчёта.
Ты вправе сказать «этого делать не надо» с аргументом — это поощряется, а не штрафуется.
## 11. Канал вопросов
Конфликт промта с кодом или доками ⇒ **пинг, НЕ интерпретация**. Промт мог устареть между написанием и
исполнением; код первичен. Настоящую развилку неси быстро.
**Правь курс через оркестратора:** mid-flight правки доезжают ТОЛЬКО релеем — владелец пере-передаёт
правку отдельным сообщением, ты эхо-подтверждаешь, и она отдельным пунктом отчёта.
## 12. Прямой канал
Механизм — `CLAUDE.md` §«Связь между сессиями»; файл **`/tmp/textmachine-channel`**. Впиши туда СВОЙ
блок первым делом, дописывая в КОНЕЦ и не трогая чужие. Перед тем как писать кому-то — сверься с
`ListAgents`: файл переживает смерть сессии, `ListAgents` нет.
⚠ Дословно: **нужной роли нет, файла нет или имя не отвечает ⇒ КАНАЛА НЕТ, и это НОРМАЛЬНЫЙ случай: НЕ
опрашивай сессии подряд.** Вопрос — секцией в отчёт.
---
## Приложение А. Известные грабли прежних холодных прогонов
Промт на холодный прогон писался ДВАЖДЫ, оба отработаны (`docs/archive/prompts/`). Третий заход (работа
C промта `BACKEND_EFFORT_HANDLE`, 02.08) закрыт владельцем как неуспешный — и **прогон там не начался
вовсе**. Техническая причина: `DeepSeek-V4-Flash-0731` сменил веса под тем же слагом, а thinking у
вендора включён по умолчанию с эффортом high.
**Слаг живой ≠ модель та же.** Перед покупкой сверь ПОВЕДЕНИЕ модели, а не только доступность слага.
`coldrun-b` заморожен ЛЕГИТИМНО: фаза C остановлена чекпойнтом по букве промта (критерий «0 пустых»
нарушен 8 из 9). Его собственная база ПУСТА — деньги «coldrun-b» потрачены в подпроектах `reprobe/*`.
Оба каталога — улика, read-only.
## Приложение Б. Готовые команды (проверены на HEAD, но пере-проверь)
```
# бинарь из HEAD + его личность
cd /home/ubuntu/projects/textmachine-main/backend && go build -o <твой путь>/tmctl ./cmd/tmctl
sha256sum <твой путь>/tmctl
# $0-глаголы, ключей не требуют, безопасны в любом порядке:
tmctl <report|status|export|build|manifest|backup|migrate> --config book.yaml
tmctl bank-apply --config book.yaml --decisions decisions.json [--dry-run]
tmctl seed-lint --seed glossary.yaml
# ⛔ ПЛАТНЫЕ:
# translate — твой глагол, см. §1в
# redrive — этот пак его НЕ заказывает НИГДЕ. Не запускай: он и платит, и разрушительно
# сбрасывает флаговую телеметрию, и принимает голую форму без селектора.
# полная стоимость из сырья — ⛔ ВСЕГДА mode=ro (§2)
sqlite3 'file:<db>?mode=ro' "SELECT printf('%.6f',COALESCE(SUM(committed_usd),0)),
printf('%.6f',COALESCE(SUM(reserved_usd),0)) FROM spend WHERE book_id='<id>';"
```