textmachine/docs/POLYGON_COLD_RUN_A_SESSION_PROMPT.md

402 lines
47 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.

# Промт: полигон-пак «ХОЛОДНЫЙ ПРОГОН A — от двери до ФАЙЛА, настоящая книга, настоящие деньги»
> ⚠ **Редакция 2 (11.09).** Первая редакция прошла опровергателя и получила **двадцать** находок, из них
> четыре денежных и три, обнулявших смысл прогона. Все внесены. Места, где я ошибался, помечены
> «⛔ ИСПР.» — читай их внимательнее прочего: там сказано, чему НЕ верить.
## 1. Какая проблема и что решит твой результат
Мы построили весь путь: гость заводит книгу → платформа берёт деньги в холд → движок переводит → человек
подписывает банк терминов → редакторская волна → выгрузка → файл. **Каждый кусок предъявлен по отдельности.
Книги, прошедшей его ЦЕЛИКОМ до конца, не было ни разу.** Это строка бэклога **16** дословно: «цена
ДОВЕДЁННОЙ ДО КОНЦА книги не измерена». Ближайшая попытка — 04.09 — дошла до скачанного EPUB, но упёрлась в
денежный потолок.
**ИСПР. 1: СУДЬИ НА ЭТОМ ПУТИ НЕТ.** Боевой конфиг `pipeline-c1.yaml` несёт ДВЕ стадии — `draft` и `edit`;
`role: judge` живёт только в `c2` и Фазой 0 прямо не исполняется (`backend/internal/config/pipeline.go:506-508`).
Там же `escalation.budget_usd: 0` ⇒ эскалация НЕ исполняется вовсе, и `gates.coverage.enabled: false` — один
из пяти гейтов выключен. Путь, который ты проверяешь: **черновик → редактор → четыре $0-гейта → выгрузка**.
Так и пиши в пре-реге; «судья» из моей первой редакции — моя ошибка.
**Твой результат:** одна настоящая книга (3 главы zh→ru) проведена от двери до файла, открытого человеком, и
**у каждого узла напечатано, ЧЕМ он предъявлен**. Плюс честный список того, что на этом пути сломано: прогон
**ничего не чинит**, он только называет.
## 2. Зона записи и git
**Зона: `eval/` + `docs/experiments/`**, пинг оркестратору — секция «Полигон» в `docs/PROGRESS.md`.
**Сессия НЕ коммитит — КРОМЕ пре-рег-фризов** (право полигона, единственное исключение в проекте).
Канон git — `CLAUDE.md` §«Git-координация мультисессий». ⛔ `backend/` и `platform/` — ЧИТАТЬ, не править;
недостающие артефакты класть **в worktree-копию**, не в главное дерево. `.env` **не читать**.
**ИСПР. 2 — АДРЕСАТ ФРИЗ-ОБЪЯВЛЕНИЯ.** Канон говорит «объявляется ВЛАДЕЛЬЦУ вслух до коммита», но владелец
отошёл, а §13 запрещает его беспокоить. Разрешаю так: **объявление идёт мне по каналу; канала нет ⇒
объявление печатается ОТДЕЛЬНОЙ секцией отчёта ДО строки фриз-коммита, и это засчитывается.**
**Правка ЗАКАЗА на лету доезжает только РЕЛЕЕМ:** я пере-передаю её отдельным сообщением, ты
эхо-подтверждаешь своими словами и выносишь отдельным пунктом отчёта.
## 3. Карта чтения — ЗАКОН, дальше только по её ссылкам
1. `CLAUDE.md` — цели владельца и гардрейлы.
2. `docs/experiments/09-pilot-protocol.md` + `eval/README.md` — устройство полигон-зоны и пре-регистрация.
3. `docs/experiments/00-provider-quirks.md`**оплаченные** провайдерские факты; ДО первого платного вызова.
4. `platform/docs/STACK_DECISIONS.md` §«Стенд разработчика» (строка 463) — рецепт стенда и инвентарь каналов.
5. Строки бэклога **16** и **224** (`docs/BACKLOG.md`, греп `^| N |`).
**Ратифицированное и замеренное — в теле промта, не искать:**
- **`D39.144`** — банк подписывается ВЕСЬ ОДНИМ ОК; резюм снимает стоп при ЛЮБОМ состоянии решений.
- **`D39.178`** — сборка выгрузки идёт `--partial`. ⛔ **ИСПР. 3: я огрубил ноту.** Полностью: неполная книга
уходит **С ПОМЕТКОЙ и маркером на месте КАЖДОЙ дыры**; «молчаливый полный файл на книге с дырой ПО-ПРЕЖНЕМУ
ЗАПРЕЩЁН»; граница `D25.1` осталась открытой. То есть дверь строит всегда, но обязана СКАЗАТЬ.
- **`D39.194` п.1** — цикл замкнут живьём 04.09, EPUB прошёл `epubcheck` 5.3.0. ⚠ Слова `complete` в теле ноты
нет: что книги, доведённой до `complete = true`, не было, говорит строка **16**, а не нота.
- **Претрейн:** 蛊真人 узнаётся моделями 4 из 5 ⇒ **пак НЕ делает утверждений о КАЧЕСТВЕ перевода.** Узлы
судятся механизмом — кадрами, флагами, леджером.
- **Норма денег полигона:** «не влезает в бюджет — СТОП и пинг, не резать молча»; подъём потолка просят при
СРАБАТЫВАНИИ стопа, не по проекции. ⚠ Носитель — ролевой промт оркестратора; атрибуцию `D39.108` п.1 я снял
11.09 фантом-гардом (в теле ноты формулировки нет).
- **Живость проверяется АКТИВНО:** один вызов редактора законно идёт до ~21 минуты
(`attempt_max_s: 1240`, `backend/configs/models.yaml:78` — ⚠ это ПОТОЛОК уровня ПРОВАЙДЕРА, не типичная
длительность). ⛔ **ИСПР. 4: `events.jsonl` и `request_log` — ОДИН носитель** (диск одного процесса), и
растущий лог не есть работа. Сторожи по РАЗНЫМ осям: число строк `request_log` (движок) **и** `book_events`
/ `run_attempts.last_seq` (платформа), плюс `systemctl show` юнита. Каденция — раз в 3040 минут, доклад
«жив» — мне, одной строкой с двумя числами.
**Какие полигонные нормы здесь действуют, а какие нет — ИСПРАВЛЕНО.** «Пороги различимости» и «запрет
судьи-родственника» не работают: судейской линзы и сравнения рук в паке нет. **Но «арм без фактора» и «декой»
работают, и я был неправ, объявив их неприменимыми** — см. §4.0 п.3 и §5 п.10. Пре-регистрация ожидаемых
исходов (§4.3) действует в полную силу.
## 4. Состав: четыре фазы. Порядок ЖЁСТКИЙ
### 4.0 Фаза 0 — $0-ДЫМ. **Делай РОВНО так, и это первая работа пака**
**Ни одного платного вызова, пока дым не прошёл целиком.**
1. **Инструмент заглушки — ОДИН, и он ШТАТНЫЙ: провайдер `kind: local` на `127.0.0.1:11434`.**
**ИСПР. 5 (две правки, вторая — МОЯ ОШИБКА, снятая полигоном 11.09).**
**(а)** Чужой `~/tm-p9-work/fake_provider.py` не брать: это двухнедельный скрипт в чужом домашнем
каталоге, а не механизм проекта.
**(б)** ⛔ **Но моё «с `local` они несовместимы, разные адаптеры» — НЕВЕРНО, и вот чем предъявлено:**
заглушка живёт В РЕПО портированной — `platform/internal/runner/translate_resnapshot_live_test.go:115`,
комментарий дословно «fakeProvider is the P9 live-probe's $0 stand-in (fake_provider.py), ported», — и
обслуживает она ровно провайдера `kind: local`. Рядом лежит готовый рендер $0-конфига
`zeroCostPipeline` (`platform/internal/runner/bankapply_live_test.go:116`), который выбирает первую модель
с `Providers[m.Provider].Kind == "local"`. ⇒ **бери эти два носителя как образец.**
**И «sed по `model:`» из рецепта стенда СЕГОДНЯ НЕДОСТАТОЧЕН** — тот же рендер снимает сверх моделей
стадий ещё четыре вещи: модели ГЕЙТОВ (`model` И `classify_model` — второй падает обратно на первый
только когда ПУСТ, и его собственный комментарий называет это уже случавшимся провалом), `escalate_to`,
`label_models`, `escalation.budget_usd`, и абсолютизирует `mining.contrast_path`.
⇒ **пин фазы 0: после рендера напечатай счёт платных слагов в получившемся конфиге — ноль, при
контрольном счёте тех же слагов в `pipeline-c1.yaml` — не ноль.** Молчащий греп и чистый конфиг иначе
неотличимы.
2. **Что дым обязан доказать ДО денег:** стенд поднят и порт отвечает ТВОЕМУ pid · гость заведён · книга
принята · прогон стартовал · кадры доехали · узел подписи ответил · резюм прошёл · **выгрузка собралась и
файл скачался** · все сборщики следов печатают то, что обещали.
3.**ПОЗИТИВНЫЙ КОНТРОЛЬ КАЖДОГО СБОРЩИКА — обязателен, стоит $0 и снимает сразу три дыры пака.**
Засей в стендовую БД заведомо НЕНУЛЕВОЙ банк и заведомо удержанный юнит и **потребуй, чтобы сборщики стали
КРАСНЫМИ**. Сборщик, не покрасневший на подсадке, в платную фазу НЕ ДОПУСКАЕТСЯ. Довод: критерии §5 п.1 и
§4.4 одинаково удовлетворяются молчащим прибором — «счётчики нулевые» и «прибор ничего не прочитал»
неотличимы, а банковый сборщик на платной руке работает первый раз в жизни (на заглушечной прозе банку
неоткуда взяться).
4.**Дым меняет ТРИ фактора разом** (провайдер + книга + база) — значит при провале платной руки он не
разделяет причину. Держи это в голове и, где дёшево, меняй по одному.
5. **Прибор §4.5 п.2 (счёт расхождения форм) строится и пинится ЗДЕСЬ**, на бесплатном материале
(`books/gu-zhenren/coldrun-v16/guzhenren-coldrun-v16.db.book.epub`), а не после траты денег.
### 4.1 Предусловия стенда — **делай РОВНО так; список пере-снят и ИСПРАВЛЕН**
1.**Артефакта контраста майнинга на ФС нет ни одного** (два независимых полных скана; контроль: под `$HOME`
просмотрено 390 083 файла). `CheckMiningContrast` откажет на старте `translate` — $0, но останется
`failed`-прогон, и следующий старт той же книги пойдёт как ПРОДОЛЖЕНИЕ. Рецепт и пин —
`docs/experiments/16-bank-mining.md:183`: jieba 0.42.1 `dict.txt`, sha256
`7197c3211ddd98962b036cdf40324d1ea2bfaa12bd028e68faa70111a88e12a8`. ⚠ **Ловушка имени:** в конфигах файл
зовётся `mining-contrast.zh.txt` (`backend/configs/pipeline-c1.yaml:139`) и резолвится ОТ КАТАЛОГА
`pipeline.yaml`, а источник — `jieba_dict_general_zh.txt`. ⛔ **И ловушка проверки: гард делает только
`os.Stat`** — рядом на диске лежит ФАЛЬШИВЫЙ кандидат `~/tm-p9-work/cfg/contrast-zh.txt` (992 байта, sha не
тот), и гард им удовлетворится. **Сверяй sha, а не имя.**
2. **jieba ставится, это НЕ блокер** (испр.: `python3 -m venv` есть, `ensurepip` даёт pip 25.1.1, pypi достижим).
3. **Кластер Postgres поднят, но БЕЗ TCP-порта:** `listen_addresses` пуст, слушает только unix-сокет.
Подключаться `-h /tmp -p 55433 -U postgres` (ОС-роль `ubuntu-26` в нём не заведена). ⛔ Кластер НЕ сносить,
чужие базы не трогать — там данные платного прогона 04.09; заводи СВОЮ.
4.**ИСПР. 6: `epubcheck`/`java` на хосте нет, и поставить их сессия НЕ может** — passwordless sudo
отсутствует, а §13 запрещает беспокоить владельца. ⇒ **валидатор EPUB НЕ обязателен.** Узел судится
`txt`-выгрузкой плюс структурным чтением EPUB (zip + `container.xml` + `nav`), и «файл ОТКРЫТ» значит
**человек прочитал первый абзац перевода и сказал, что это связный русский текст**, а не `cat`. Ставишь
`epubcheck` без sudo из тарбола в `~/.local` — законно, версию назови числом.
5. **Порт может отвечать ЧУЖИМ процессом** (`PD-447`): опознавай стенд по pid-на-порту. Сегодня стенд не
поднят вовсе: `tmplatformd`-процессов 0, дефолтный `127.0.0.1:8080` свободен.
6.**Годится ТОЛЬКО бинарь из фриз-воркри.** Настоящих стендовых копий `tmctl`/`tmvet`/`tmmutate`**34**
(вне `$HOME` и `/tmp` — 0); среди них бинарь от 24.08 на `PATH` (`~/.local/bin/tmctl`), носитель `PD-432`.
Копии заводятся и в скретчпадах ⇒ негоден ЛЮБОЙ бинарь не из фриз-воркри.
7. ⛔ **ИСПР. 7 — ПЯТЬ ПЕРЕМЕННЫХ ОКРУЖЕНИЯ РЕШАЮТ, СУЩЕСТВУЕТ ЛИ ПУТЬ ВООБЩЕ, и в первой редакции их не
было.** `TM_PLATFORM_EXPORT_FORMATS` по умолчанию **ПУСТ** ⇒ все три ручки выгрузки НЕ монтируются, и
последняя треть пути для тебя не существует (404) · `TM_PLATFORM_ENGINE_BIN` пуст ⇒ нет ни прогонов, ни
интейка · `TM_PLATFORM_BOOKS_DIR` пуст ⇒ нет `POST /v0/books` · `TM_PLATFORM_ENGINE_KEYS_PATH` — см. п.8.
**ИСПР. 19: `TM_PLATFORM_ENGINE_CEILING_ARG` из этой пятёрки ВЫЧЕРКНУТ — я был неправ.** У переменной
есть дефолт `--ceiling-usd {{usd}}` (`platform/internal/config/config.go:550`), демон печатает его на буте
как `source=default`, и прогон стартует, стоит и завершается, пока она не задана вовсе. Замерено полигоном
исполнением на стенде 11.09. Остальные четыре в силе.
**Пин ДО фриза: `GET /v0/capabilities` показывает непустой `export_formats`.**
8.**Ключи провайдера.** Значение `TM_PLATFORM_ENGINE_KEYS_PATH` даю я; в окружении его сейчас нет, и
правдоподобного кандидата на диске тоже. **Нет значения ⇒ СТОП ДО фриза** — платного пути без него не
существует, а `.env` читать запрещено гардрейлом.
9. **Субъект и деньги заводятся НА МАШИНЕ, не по HTTP:** самореги нет (маршрутов с `auth` в таблице — 0 из
19), пополнения по HTTP нет (`credit`/`grant`/`balance` — 0 из 19). Путь: `POST /auth/dev-login` при
`TM_PLATFORM_DEV_LOGIN` (тогда деплой не может быть с OIDC) либо `tmplatformctl token issue`; деньги —
`tmplatformctl grant --user <id> --usd <amount>`.
10.**Каталог книги `coldrun-v16/` УЖЕ занят прошлым прогоном** — там `project.db` (68 строк `request_log`
на $0.436), `events.jsonl`, сайдкары банка, `bin/tmctl` без VCS-штампа, и `book.yaml`, указывающий в
ЧУЖОЙ домашний каталог. **Исходник читается ТОЛЬКО как файл; рабочий каталог платной книги — новый и
пустой; шаблон `book.yaml` оттуда НЕ переиспользовать.**
### 4.2 Фриз — **делай РОВНО так**
**Фриз = sha коммита ПЛЮС бинари, собранные из ЛОКАЛЬНОГО КЛОНА** (`git clone <репо> <путь>`). Довод — не
«дерево грязное», а **главное дерево движут параллельные сессии**: в нём прямо сейчас работают движковая и
платформенная. Драйвер перед каждым платным шагом печатает `go version -m` и ОТКАЗЫВАЕТ при `vcs.modified=true`,
чужом revision **или ОТСУТСТВИИ строк `vcs.*`**.
⛔ **ИСПР. 18 — МОЙ ЦЕНТРАЛЬНЫЙ ГЕЙТ БЫЛ ПИНОМ, УТВЕРЖДАЮЩИМ МОЛЧАНИЕ. Замерено полигоном 11.09, я
пере-проверил своим прибором.** Первая редакция велела собирать из `git worktree add`. Бинарь, собранный в
ЛИНКОВАННОМ воркри, не несёт VCS-штампа **ВООБЩЕ** — не `vcs.modified=true`, а НИ ОДНОЙ строки `vcs.*`
(24 строки `go version -m`, из них `vcs.*` — 0). Причина: у линкованного воркри `.git` — ФАЙЛ, и поиск корня
VCS в Go его пропускает; выше по дереву репозитория нет, поэтому сборка не падает, а **тихо выходит без
штампа** — тот же результат, что от запрещённого `-buildvcs=false`, только без единого предупреждения.
⇒ правило «отказывать при `vcs.modified=true`» на воркри-бинаре выполнялось ВСЕГДА и не проверяло ничего.
**Контроль, доказывающий, что механизм на этом хосте жив:** та же сборка из ГЛАВНОГО дерева даёт
`vcs.revision=…` и `vcs.modified=true`.
**Фриз-дерево — ЛОКАЛЬНЫЙ КЛОН:** у него `.git` настоящий КАТАЛОГ, и бинари несут
`vcs.revision=<sha> · vcs.modified=false` (проверено на всех трёх). Гейт получает зубы: «штампа нет» и
«штамп чужой» становятся отличимы от «штамп мой и чистый».
**И в клоне НЕТ `books/`** — внешний репозиторий держит их в `.gitignore`, книги версионируются ОТДЕЛЬНЫМ
репозиторием. Исходник читается из главного дерева как ФАЙЛ (read-only), а его провенанс — пара sha256
(срез и оригинал) в пре-реге. Артефакт контраста кладётся в КЛОН, рядом с его `pipeline.yaml`.
**ИСПР. 8 — ВТОРОЙ СПОСОБ ОБНУЛИТЬ ФРИЗ-ГЕЙТ, и он остаётся в силе для КЛОНА тоже.** Напоровшись на
посторонний `/tmp/.git`, сборка падает с «error obtaining VCS status», и штатное лечение
`GOFLAGS=-buildvcs=false` **стирает штамп целиком** (эррата `D39.202`). ⇒ **перед сборкой
`test ! -e /tmp/.git`; `-buildvcs=false` в фриз-сборке ЗАПРЕЩЁН; фриз-дерево — вне репозитория и вне общего
скретчпада.** ⚠ И штамп снимается **на КАЖДУЮ попытку**, а не один раз:
резюм может сменить бинарь под тем же прогоном (`TM_PLATFORM_RESUME_MAY_CHANGE_ENGINE`, колонка
`runs.engine_binary`) — сверяй по `run_attempts`, а не по тому, что лежит на PATH в конце.
**В фриз-коммит:** `docs/experiments/24-<имя>.md` — пре-рег с таблицей ожидаемых исходов (§4.3), sha256
исходника и **sha256 ФАКТИЧЕСКОГО pipeline-YAML** (платформа берёт файл ЦЕЛИКОМ через `TM_PLATFORM_BOOK_TEMPLATE`,
пер-полевого оверрайда нет ⇒ «прогон на c1» без sha — утверждение без носителя), список глав с `units_total`;
`eval/<каталог>/` — драйвер, сборщики следов и сверка; шаблон `book.yaml` с абсолютными путями в worktree.
**Без пере-фриза** (но печатается в отчёт): грант, `TM_PLATFORM_HOLD_FACTOR_PERCENT`, TTL выгрузки.
**Граница:** меняется ЧТО покупается или чем судится — новый фриз; меняется СКОЛЬКО тратить — объявление.
⚠ Правка собственного драйвера после фриза — тоже новый фриз.
### 4.3 Пре-регистрация ожидаемых исходов — **главный рубеж пака**
**До первого платного вызова** в фриз-коммите лежит таблица: **узел → что ДОЛЖНО произойти, числом или
строкой → чем предъявлено → что считается ПРОВАЛОМ узла.**
**Следы — по РАЗНЫМ носителям:** **(1) Postgres стенда** (`books`·`chapters`·`units`·`runs`·`run_attempts`·
`reservations`·`credit_ledger`·`book_events`·`exports`); **(2) диск движка и systemd** (`events.jsonl` с
кадрами `hello/progress/unit_done/bank_stop/ceiling/spend/finished`; `project.db`/`request_log` — читать
`mode=ro` можно НА ХОДУ, WAL это разрешает; сайдкары `.bank.json`, `mined-signature.yaml`, `.bank-stop.txt`;
**systemd-маркер выхода** `<StateDir>/runs/<runID>-<attempt>.exit` — это маркер ЮНИТА systemd, не
переводческого юнита); **(3) HTTP и файл** — ответы ручек, SSE, скачанные БАЙТЫ и их sha256.
**ИСПР. 9 — И ЧЕСТНО ПРО НЕЗАВИСИМОСТЬ: следов не три.** Postgres платформы **материализуется из
`events.jsonl` движка** (`Sink materializes a stream into the reporting database… not an event store`), а
текст, сверку и файл платформа получает теми же `tmctl export/status/build`. ⇒ **«три следа сошлись» означает
"одна БД согласна сама с собой"**. По-настоящему независимы только скачанные БАЙТЫ и их sha256; биллинга
провайдера у нас нет. Так и напиши в пре-реге — это не отменяет сверку, но меняет, что она доказывает.
**ИСПР. 10 — ДЕНЕЖНОЕ РАВЕНСТВО НАЗВАНО НЕТОЧНО.** Кадр несёт `committed_micro_usd` (целые микро-доллары),
а `committed_usd` — поле ДРУГОГО носителя, `tmctl status --json`; конверсия округляет ВВЕРХ. `request_log`
объявлен ТЕЛЕМЕТРИЕЙ, а не источником денег («деньги живут в spend/checkpoints»), и редрайв удаляет чекпойнты —
это законный класс расхождения. ⇒ печатай в МИКРО-долларах и как ПРОВЕРКУ, а не тождество:
Σ `request_log.cost_usd``spend.committed_micro_usd` ≈ Σ `credit_ledger` settlement ≈ (грант баланс);
расхождение — назвать, а не подогнать. **Открытых резерваций на конце — 0** (холд дебетует баланс отдельной
строкой `kind='hold'`).
### 4.4 Фаза 2 — платный прогон
**Книга:** `books/gu-zhenren/coldrun-v16/guzhenren-ch1-10.gb18030.txt` (57838 байт, sha256
`0b5f9b0266d8c32ac717a41211d2ed15d4f2a07f37a701fb686e255ab6c89f37`, 10 глав маркерами `第一节…第十节`).
⚠ Имя эксперимента — НЕ `coldrun-a`: каталог занят прогоном 24.08.
**ИСПР. 11 — ОБЪЁМ ЗАКАЗА БЫЛ ДВУСМЫСЛЕН, реши и объяви в пре-реге.** Интейк главами резать НЕ умеет
(форма несёт `title`/`source_lang`/`target_lang`), объём покупается членом `chapters` на старте прогона.
Значит либо **(а)** режешь файл до 3 глав и грузишь срез — тогда `units_total` книги и заказ совпадают; либо
**(б)** грузишь весь файл и заказываешь 3 главы — тогда узел выгрузки судится по `ordered_chapters` /
`delivered_chapters`, а НЕ по `units_total`. **Рекомендую (а)**: сверка честнее и дешевле.
⚠ И `delivered` — это `finished.volume.delivered`, а секции `volume` у цельнокнижного заказа НЕТ вовсе
(платформа не передаёт `--max-units`); плюс `Delivered` считает только юниты, «never completed before» — после
резюма это уже не `units_total`.
**ИСПР. 12 — ГЛАВЫ 13 ТОЛЩЕ СРЕДНЕЙ.** Замер по БД прошлого прогона того же исходника: глава 1 — 4 чанка,
глава 2 — 3, глава 3 — 2, остальные по 2 (контроль: всего 23 строки `chunk_status`). **9 из 23 чанков = 39 %
книги в 30 % глав.** ⚠ И пин «≥1 глава с `units_total ≥ 2`» из первой редакции охраняет то, чего не бывает
(таких глав все десять) и НЕ охраняет то, что случится — недооценку объёма. Замени пин на **«сумма
`units_total` по заказанным главам совпадает с пре-регом»**.
**УЗЕЛ ПОДПИСИ БАНКА — самое ценное место прогона, и здесь я ошибался дважды.**
**ИСПР. 13a: читатель у секции предложений ПОСТРОЕН.** Платформа считает `bank_terms` (`count(*)` и
`filter (where status='approved')`), а строки туда пишет `SaveBank` из движкового сайдкара. Наблюдённый ноль
объясняется МОМЕНТОМ обновления: банк пересчитывается, только когда книга «в покое» (нет прогонов с
`finished_at is null`), а при банк-стопе статус уходит в `awaiting_bank` РАНЬШЕ закрытия строки прогона.
**ожидаемый исход: платформа показывает ноль ПОКА строка прогона не закрыта и НЕнулевое ПОСЛЕ.** Ноль после
закрытия — дефект; ненулевое до — тоже факт. ⛔ Прежняя формулировка («узел засчитан, только если платформа
показала ноль») НАКАЗЫВАЛА исправное поведение — снята.
**ИСПР. 13b: улику уничтожает резюм, который пак сам заказывает.** `proposed[]` в `.bank.json` берётся из
поля в памяти ОДНОГО процесса, а `exportBank` зовётся в том числе на СТАРТЕ каждого прогона и перезаписывает
файл атомарно. ⇒ **сними sha256 и КОПИЮ `<project_db>.bank.json` в момент `awaiting_bank`, ДО
`bank/corrections` и ДО резюма.** Более живучие улики того же факта — `mined-signature.yaml` и `.bank-stop.txt`:
они пишутся при каждом заходе в майнинг.
**ИСПР. 13c: банк-стопа может не случиться вовсе.** Стоп требует трёх условий разом: флаг `--verify-bank`
И наличие edit-волны И **кластер, которого не показывал ни один прежний стоп** (движок помнит показанные
поверхности). Иначе — авто-продолжение с неподписанным банком, и **второй прогон по той же книге не
остановится никогда**. Это девятый способ выродиться: узел не пройден не потому, что сломан, а потому, что не
наступил. Назови исход явно.
**Дверь решения зовётся `POST /v0/books/{bookId}/bank/corrections`**, `action ∈ approve|decline`, пачкой
(персональных ручек по терминам нет, таблица `bank_decisions` мертва). «Подпись» завершается одним
`POST /v0/runs/{runId}/resume`. **Решение, которое принимаешь ты:** провести ОДНО настоящее решение через
дверь (рекомендую — иначе дверь правок остаётся непройденной) либо резюмить неподписанным. Объяви ДО прогона.
**Чего делать НЕЛЬЗЯ:** чинить найденное · править `backend/`/`platform/` · менять срез или пайплайн после
фриза без нового фриза · тихо пополнять баланс · интерпретировать провайдерскую аномалию без вендор-доки.
### 4.5 Фаза 3 — $0 после прогона
1. **Сверка каждой строки пре-рег-таблицы**: ожидание → факт → сошлось/нет → чем предъявлено.
2. **Механический счёт расхождения форм по ОТГРУЖЕННОМУ тексту** (прибор построен в фазе 0, §4.0 п.5):
сколько канонических терминов отданы более чем одной передачей и в каких главах. ⚠ Это МЕХАНИЧЕСКИЙ счёт,
не суждение о качестве. Он нужен, потому что такой меры по финальному тексту сегодня не существует вовсе
(строка бэклога **406**).
3. **Список сломанного** — строкой на находку: что · где · чем предъявлена · «замерено / прочитано /
подозрение». Ряды бэклога заведу я.
## 5. Как прогон вырождается — десять способов и их снятие
1.**Дверь выгрузки ВСЕГДА строит** (`--partial`). **Снятие:** `exports.complete = true` И нулевые счётчики
отчёта сборки И `finished.outcome` в списке успеха И все `units.state='translated'`.
**ИСПР. 14: ни `complete`, ни счётчики на API НЕ ОТДАЮТСЯ.** `wireExport` несёт 7 полей, и запрет
умышленный («what does NOT cross: … whether the file came out whole»). ⇒ `complete` читается **из
Postgres**, счётчики — **из stdout `tmctl build` или лога платформы**. ⚠ И проверяй не пять полей, а
**семь**: к `pending/withheld/incomplete/stale/ghost` добавь `stale_unknown` и `config_drift` — под дрейфом
конфига `stale_units` механически 0, и `complete: true` при этом ЗАКОНЕН. Ноль без `stale_unknown=false`
не доказывает ничего.
2. **Срез не тот, что в пре-реге.** Снятие: $0-манифест ДО фриза (`tmctl manifest --json` даёт `units_total` и
по книге, и по главе; ⚠ он ПЕРЕЗАПИСЫВАЕТ `<project_db>.manifest.json` и требует записываемого каталога),
сверка после интейка, расхождение — стоп ДО оплаты.
3. **Узел подписи** — §4.4, три исправления.
4. **Деньги мимо сверки** — §4.3 ИСПР. 10; строки `request_log` с непустым `err` или `finish_reason ≠ stop`
перечисляются поимённо. (На опорном прогоне 08.09 таких было **32 % трат**.)
5.**ИСПР. 15 — СТОРОЖ ПОТОЛКА СМОТРЕЛ НА СТРОКУ, КОТОРОЙ НЕ БУДЕТ.** `credit_exhausted` «больше не
производится для собственного потолка прогона» — это счёт АККАУНТА; потолок прогона даёт
**`run_limit_reached`**. Полный словарь: `run_limit_reached · credit_exhausted · daily_ceiling ·
ceiling_unknown`. ⇒ **ЛЮБОЕ непустое `paused_reason` = СТОП и пинг.** Прогон при этом не `failed`
контракт это запрещает.
6. **Чужой стенд и ложная зелень:** порт чужого процесса · ничьи базы · старый `tmctl` · `vcs.modified`.
Снятие: pid-на-порту · своя база · штампы по КАЖДОЙ попытке · контрольные числа **и по `runs`, и по
`run_attempts`** (резюм после банк-стопа гарантирует вторую попытку — «прогонов = 1» этого не видит).
7. **Отказ на старте не бесплатен для ЭКСПЕРИМЕНТА** (артефакт контраста): $0 по деньгам, но после `failed`
книга уже не холодная. Снятие: дым и предусловия ДО загрузки платной книги; платной книге — свежий id.
8. **Претрейн** — никаких суждений о качестве.
9.**НОВОЕ: прогон может НЕ КУПИТЬ НИЧЕГО и выйти успешным — реплей чекпойнтов.** `RequestHash` адресует
чекпойнт, попадание пропускает вызов провайдера и стоит $0 (колонка `request_log.tm_hit` — «ответ взят из
чекпойнта»); гварда «`project_db` обязан быть новым» в коде НЕТ. Если платная книга ляжет на путь, где
остались чекпойнты дыма под тем же снапшотом, прогон пройдёт зелёным почти бесплатно и измерит пустоту.
**Снятие:** `select distinct model_actual from request_log` **не содержит `local-*`** — это НЕСУЩИЙ пин;
`tm_hit` печатается **с разбивкой по попыткам**, а не суммой; `test ! -e <project_db>` до старта — дешёвая
добавка.
**ИСПР. 20: «`tm_hit = 0` после прогона» — НЕВЕРНО для прогона с банк-стопом, и это моя ошибка.**
Замерено полигоном на дыме: 3 строки из 13 пришли с `tm_hit=1`, и это ЗАКОННО — резюм переигрывает
чекпойнты СВОЕЙ ПЕРВОЙ попытки, за которые уже заплачено. Суммарный ноль тут означал бы, что стопа не
было. Различает не сумма, а разбивка: попадания внутри одной попытки — подозрение, попадания второй
попытки в чекпойнты первой — норма.
10.**НОВОЕ: молчащий прибор удовлетворяет три центральных критерия сразу** (§5 п.1, §4.4, §4.3).
**Снятие — позитивный контроль сборщиков в фазе 0** (§4.0 п.3). Если вносить в пак что-то одно, вносить
это.
**Мандат самопроверки ИСПОЛНЕНИЕМ:** ревью СВОЕГО кода драйвера, СВОИХ запросов и ПОЛУЧЕННЫХ результатов.
Субагентов спавнить **РАЗРЕШЕНО и требуется**, модель задавай ЯВНО. ⛔ Отрицательный замер печатает
КОНТРОЛЬНУЮ ВЕЛИЧИНУ рядом с нулём. ⛔ **Критик полноты — на КАЖДУЮ фазу.**
## 6. Предметные оси ревью
**Полигон/замеры:** пере-вывод чисел из СЫРЬЯ; вывод на агрегате до вскрытия единиц запрещён; ⚠ про
«независимость сигналов» — читай §4.3 ИСПР. 9: здесь она ограничена, и это надо не обходить, а назвать.
**Деньги:** из сырого леджера двумя путями; леджер — нижняя граница.
## 7. Записка-план ДО работы
До первой команды — мне ≤15 строк: как ведёшь фазы · где ждёшь сопротивления · что считаешь спорным в заказе.
Пре-рег-таблица §4.3 присылается ОТДЕЛЬНО и ДО фриза — я её читаю. Канала нет — обе идут секциями отчёта.
## 8. Заявление = команда
Каждое число и каждая категорика — с командой и её выводом. **Число называет ДЕРЕВО и ПОПУЛЯЦИЮ.**
## 9. Эхо-протокол старта
ДО первой команды — мне первым действием, по адресу из `/tmp/textmachine-channel` (`SendMessage`, роль
`ОРКЕСТРАТОР`), ≤10 строк: скоуп · инварианты · не-делать — **СВОИМИ словами**.
## 10. Что НЕ удалось — обязательная секция отчёта
Что не проверено · что не воспроизвелось · где данных не хватило · какой узел объявлен непройденным и почему.
**«Не измерено» вместо догадки.**
## 11. Канал вопросов и право отказаться
Конфликт промта с кодом — **пинг мне, НЕ интерпретация**. Право сказать «этого делать не надо» с аргументом —
есть. Всё про деньги сверх названного — через меня.
## 12. Прямой канал
`CLAUDE.md` §«Связь между сессиями»: `/tmp/textmachine-channel`, свой блок первым делом, живость — `ListAgents`.
Роли нет или имя не отвечает ⇒ **КАНАЛА НЕТ, это НОРМАЛЬНЫЙ случай**, вопрос — секцией в отчёт.
## 13. Критерий завершённости
Работа завершена, когда: **каждая строка пре-рег-таблицы имеет исход** · **у каждого пункта §4 есть исход**
(сделано · не делаю с доводом · пинг) · **круги СОШЛИСЬ** — последний проход критика полноты не дал НОВЫХ
находок · файл скачан и ПРОЧИТАН человеком (см. §4.1 п.4 — что это значит) · деньги сведены и расхождения
названы · находки размечены «замерено / прочитано / подозрение» · фризы и девиации названы · артефакты в
ДЕРЕВЕ · явное **«работа завершена, править не планирую»**.
**Пак доводишь до конца ТЫ.** Владельца не беспокоить, меня не тегать «просто так».
## Деньги — ДВА числа, и первое ИСПРАВЛЕНО
**ИСПР. 16: ожидание $0.30 было занижено дважды.** (а) главы 13 несут 39 % книги, а не 30 %; (б) опорный
замер 08.09 снят на стенде с книжным потолком терминологии **$0.07**, тогда как у `c1` он **$2.00** — в 28 раз
выше. ⇒ **ожидание $0.350.50, хвост до $0.65.**
**ИСПР. 17 — САМОЕ ДОРОГОЕ: «грант обязан покрывать холд, иначе допуск откажет» — НЕВЕРНО.** При нехватке
баланса холд САМ ужимается ровно на книжный бонд и прогон **допускается** с `bondFunded = false`
(`platform/internal/pricing/pricing.go:198-224`: `if base+bookOnce <= balance { … } return base, false`;
пин зоны — баланс $1.00, книга 5 глав, бонд $2.00 → `covers_all`, `AffordableChapters = 5`,
`BondFunded = false`). Отказ достижим только при балансе ниже ≈$0.26. **Следствие:** грант $2.00 даёт
стартующий прогон, чей `--ceiling-usd` не несёт бонда, и терминологическая консолидация с классификатором
ловят отказ резерва — «потолок, слишком малый для них, стоит КАЧЕСТВА, а не прогресса». Пре-рег измерит
пайплайн без фазы майнинга и не узнает об этом.
**грант ≥ 1.25×expected + step_max + 2.00. $2.5 безопасно, $2.3 впритык, $2.0 — ТИХАЯ ДЕГРАДАЦИЯ.**
**Пин пре-рега: `term_consistency_funded: true` в ответе ручки, иначе СТОП ДО оплаты.**
**Стоп-правила — каждое остановка и пинг, а не решение сессии:**
- фактическая трата дошла до **$1.00** (порог поднят с $0.80: один оборванный ответ его сорвал бы);
- `paused_reason` НЕПУСТО — любое из четырёх значений;
- срез не совпал с пре-регом — стоп ДО оплаты;
- `term_consistency_funded: false` — стоп ДО оплаты;
- бинарь не из фриза — стоп до устранения.
## Отчёт
`docs/experiments/24-<имя>.md` (пре-рег + результаты) + `eval/<каталог>/` + секция «Полигон» в
`docs/PROGRESS.md` + сообщение мне: таблица «узел → ожидание → факт → чем предъявлено» · деньги с названными
расхождениями · список сломанного с разметкой уверенности · что сказал критик полноты по КАЖДОЙ фазе ·
секция §10 · **что ты сам считаешь слабым местом прогона**.
⛔ Последний абзац отчёта — план или обещание? Значит работа не кончена: сделай сейчас.