textmachine/docs/COLD_RUN_B_SESSION_PROMPT.md

488 lines
56 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.

# Промт: сквозной пак «ХОЛОДНЫЙ ПРОГОН B — платформа → бэкенд → ФАЙЛ, ВТОРОЙ раз»
> Выдан оркестратором №23 (сессия `textmachine-11`) 11.09. Пак **ПЛАТНЫЙ**, санкция владельца получена
> дословно («разрешаю») на прямой вопрос с числами: **ждём ≈$0.42, машинный кап ≈$2.5 холда**. Носитель
> санкции — `docs/NEAR_TERM_PLAN.md` §«Решения владельца, уже полученные».
## 1. Какая проблема и что решит твой результат
Месяц назад движок умел переводить, но никто не видел, как платформа ведёт настоящую книгу от двери до
файла. 11.09 это увидели один раз — **прогон A**: три главы `蛊真人`, 27 платных вызовов, **$0.419423**, книга
дошла до EPUB. Один прогон дал знак, но не величину.
**Твой прогон B покупает четыре вещи, которых нет ни у кого, и каждая — не «ещё раз то же самое»:**
1. **ДВЕ миграции схемы ни разу не шли на живом прогоне** — только в тестах, и база прогона A стоит на
`schema_version` **16**. `v17``wave_selection` (`backend/internal/store/migrate.go:484`=`CREATE TABLE IF NOT EXISTS wave_selection`): что каждая
волна ВИДЕЛА в банке, ось, которой у `retrieval_state` нет. `v18`**`reasoning_in_completion`**
(`migrate.go:519`), и ⛔ **это НЕ `reasoning_tokens`**: тот лежит в базовой схеме как
`INTEGER NOT NULL DEFAULT 0` (`migrate.go:94`) и на DeepSeek равен нулю ПО ПОСТРОЕНИЮ — адаптер
намеренно оставляет его нулём и уводит присланное провайдером число в новую колонку
(`provider_openai.go`, ветка `ReasoningSubset`). Новая колонка **НУЛЛАБЕЛЬНА и трёхзначна: `NULL`
вопрос не задан · `0` — измеренный ноль · `>0` — думанье**, и `DEFAULT 0` не поставлен намеренно, иначе
дефект вернулся бы первой же строкой. **Приёмка спрашивает ТРЁХЗНАЧНОСТЬ, а не «не ноль».**
2. **Прибор консистентности впервые даст числа С ЖИВОГО ПРОГОНА.** Сегодня все `I1`/`I2` получены
ПОВТОРНЫМ чтением одной и той же сохранённой базы прогона A. Пере-скан не то же, что прогон: он не
проходит через запись.
3. **Платформенный пак (идемпотентный резюм, терминальное состояние) проверяется СКВОЗЬ,** а не юнитами.
4. **Вторая точка по денежной дыре — но ЧЕСТНО о том, что она даёт.** Весь довод «четверть денег купила
пустоту» стоит на ОДНОМ прогоне. ⚠ И не обманывайся ссылкой на квирки §3д: там сказано, что парный
дизайн ловит эффект ×2.5 за **13 пар**, ×2.0 за 22, ×1.4 за 93 — **n = 2 этой нормы не выполняет и
выполнить не может**. ⇒ B покупает не величину, а **пре-регистрированный знаменатель и проверку ЗНАКА**.
Написать в отчёте «25.4 % подтверждено» по двум розыгрышам — ошибка, и я её не приму.
**И вот чего твой прогон НЕ покупает, чтобы ты не потратил на это ни цента:** он **НЕ** отвечает на
вопрос «безопасно ли редактору думать меньше» (ряд 433). Это парный дизайн на повторах с армом без
фактора, он стоит отдельных денег и отдельного заказа. Твой прогон идёт на БОЕВЫХ настройках как есть.
## 2. Зона записи и git
**ЭТОТ ПАК НЕ ПРАВИТ КОД НИ ОДНОЙ ЗОНЫ.** Ни `backend/`, ни `platform/`, ни `frontend/`. Найдёшь дефект —
**пингом мне, а не правкой**: прогон, чинящий найденное, перестаёт быть холодным и теряет право сравниваться
с A.
- **Ты не коммитишь вообще ничего.** Пре-рег и улики передаёшь мне, я лендлю. Порядок жёсткий:
пре-рег готов → пинг мне → **я коммичу фриз** → и только после этого первый платный вызов.
- Рабочее дерево прогона живёт **вне репозитория и вне общего скретчпада** (его чистит не только твой
процесс). Предлагаю `~/tm-coldrun-b`.
-`books/` версионируются ОТДЕЛЬНЫМ репозиторием; в клоне их нет. Исходник читается из главного дерева
как ФАЙЛ, read-only.
## 3. Карта чтения — ЗАКОН, дальше только по её ссылкам
1. `CLAUDE.md` — целиком (ты его уже прочёл, если попал сюда правильно).
2. `docs/experiments/00-provider-quirks.md`**перед первым платным вызовом**, §3д особенно.
3. `docs/architecture/05-decisions-log.md`**только тела `D39.247`** (акт прогона A: чем он предъявлен и
где остался слеп) и **`D39.251`** (что именно построено в наблюдаемости денег и почему лекарство
выключено).
4. `platform/README.md` — как поднимается стенд.
5. Архивный промт прогона A — `docs/archive/prompts/POLYGON_COLD_RUN_A_SESSION_PROMPT.md`. ⛔ **Инструкции
оттуда НЕ ИСПОЛНЯЮТСЯ** (архив), но §4.1 и §4.2 несут ПЕРЕ-СНЯТЫЕ предусловия стенда и механику фриза,
и переписывать их сюда значило бы завести копию, которая стареет молча. Читай как СПРАВКУ и проверяй
каждое утверждение своим прибором: часть из них снята исправлениями ПОСЛЕ выдачи.
Код-факты добывай сам. `file:line` ниже — отправные точки, а не истина: **код первичен**.
## 4. Состав. Порядок фаз ЖЁСТКИЙ
### 4.0 Фаза 0 — $0-ДЫМ. **Делай РОВНО так, и это первая работа пака**
**Ни одного платного вызова, пока дым не прошёл целиком.** Довод не гигиенический: первая же
инфраструктурная осечка на платной руке сожжёт «холодность» книги — прогон уйдёт в `failed`, и следующий
старт той же книги пойдёт как ПРОДОЛЖЕНИЕ, а не как холодный.
⛔ **И ровно поэтому — дописано релеем 11.09 по возражению исполняющей сессии: ДЫМ ИДЁТ НЕ ПО ТОЙ КНИГЕ И
НЕ В ТОЙ БАЗЕ, что платный прогон.** Иначе холодность сгорит тем самым механизмом, ради защиты которого
§4.0 и написан: платный старт пойдёт продолжением дымовой книги. Отдельная книга либо отдельная база —
выбирай, но назови в пре-реге. ⚠ Это же частично снимает §5.2 (дым меняет три фактора разом).
Дым обязан доказать: стенд поднят и порт отвечает ТВОЕМУ pid · гость заведён · деньги начислены · книга
принята · прогон стартовал · кадры доехали · узел подписи ответил · резюм прошёл · **выгрузка собралась и
файл скачался**.
⛔ **И вот ловушка, на которой этот пункт разваливается, если о ней не сказать: `zeroCostPipeline`
(`platform/internal/runner/bankapply_live_test.go`) только ПЕРЕПИСЫВАЕТ конфиг на провайдера `kind: local`
он никого не ОБСЛУЖИВАЕТ.** Единственный слушатель на `127.0.0.1:11434` в дереве — внутритестовая заглушка
(`platform/internal/runner/translate_resnapshot_live_test.go`, греп `net.Listen`), а на хосте локальной
модели нет вовсе. ⇒ **тебе РАЗРЕШЕНО поднять свою заглушку на 11434** (портировав ту же, или исполнив
несущий её живой тест) — и это не обход, а штатный путь. ⚠ Не сделав этого, ты упрёшься в отказ соединения
посреди дыма, и дешёвым выходом станет «дым неприменим, иду за деньгами» — ровно то, ради предотвращения
чего §4.0 существует.
**Пин фазы 0 — ЗАГРУЗЧИК, а не текстовый скан.** Счёт платных слагов в получившемся конфиге оставь
КОНТРОЛЬНОЙ величиной (ноль против ненулевого счёта тех же слагов в `backend/configs/pipeline-c1.yaml`), но
**сам пин — движок, который этот файл ЗАГРУЗИЛ бесплатным глаголом**. Довод замерен и стоил платформенной
зоне круга приёмки: её рецепт был «проверен гардом» — сканом имён — и производил файл, который движок
отказывается грузить (`escalate_to must differ from the primary model`, `EXIT=10`). Имя в конфиге и
загружаемый конфиг — разные предметы.
### 4.1 Предусловия — **проверь КАЖДОЕ своим прибором, список от прогона A мог устареть**
Известное на 11.09 (справка, не истина): локальной модели на машине НЕТ (ни `ollama`, ни слушателя на
11434) ⇒ настоящие узлы проходятся только платно · кластер Postgres поднят на `-h /tmp -p 55433 -U postgres`,
ОС-роль `ubuntu-26` в нём НЕ заведена · четыре переменные окружения решают, существует ли путь вообще
(`TM_PLATFORM_EXPORT_FORMATS` пуст по умолчанию ⇒ ручки выгрузки не монтируются, и последней трети пути для
тебя нет) · `epubcheck`/`java` на хосте нет и поставить их нельзя.
**АРТЕФАКТ КОНТРАСТА МАЙНИНГА — и здесь я в первой редакции ПЕРЕВЕРНУЛ факт, поймал опровергатель.**
`CheckMiningContrast` делает **ТОЛЬКО `os.Stat`** (`backend/internal/config/pipeline.go:558`=`func (p *Pipeline) CheckMiningContrast`) —
никакой sha он не сверяет и **удовлетворится фальшивым кандидатом**, который лежит рядом на диске
(`~/tm-p9-work/cfg/contrast-zh.txt`, 992 байта). ⇒ **sha256 сверяешь ТЫ, руками**: рецепт и эталон —
`docs/experiments/16-bank-mining.md:183`=`Словарь-артефакт (контраст)` (jieba 0.42.1 `dict.txt`,
`7197c3211ddd98962b036cdf40324d1ea2bfaa12bd028e68faa70111a88e12a8`). ⚠ И ловушка имени: в конфигах файл
зовётся `mining-contrast.zh.txt` и резолвится ОТ КАТАЛОГА `pipeline.yaml`, а источник —
`jieba_dict_general_zh.txt`. **Неверный артефакт молча меняет контур банка, то есть портит ровно те числа
консистентности, ради которых B и покупается.**
**ПИН ДО ФРИЗА, дешёвый и обязательный: `GET /v0/capabilities` показывает НЕПУСТОЙ `export_formats`.**
Выгрузка гейтится ТРЕМЯ условиями сразу (`platform/internal/config/config.go`, греп `ExportsEnabled`), а не
одной переменной, и проба возможностей — единственный дешёвый способ узнать, что выполнены все три, ДО того
как двинутся деньги.
**Ключи провайдера.** Значение `TM_PLATFORM_ENGINE_KEYS_PATH` даю я по твоему пингу. **Нет значения ⇒
СТОП ДО фриза.** `.env` читать запрещено гардрейлом.
### 4.2 Фриз — **делай РОВНО так**
**Фриз = sha коммита ПЛЮС бинари, собранные из ЛОКАЛЬНОГО КЛОНА** (`git clone <репо> <путь>`). Довод —
главное дерево движут параллельные сессии.
**Не `git worktree`.** Бинарь из ЛИНКОВАННОГО воркри не несёт VCS-штампа **ВООБЩЕ** (замер: 24 строки
`go version -m`, из них `vcs.*` — 0), потому что `.git` воркри — ФАЙЛ и поиск корня VCS в Go его пропускает.
⇒ правило «отказывать при `vcs.modified=true`» на таком бинаре выполняется ВСЕГДА и не проверяет ничего.
У клона `.git` — настоящий каталог, и штамп живой. **Третье условие отказа обязательно: отсутствие строк
`vcs.*`.** Перед сборкой — `test ! -e /tmp/.git`; `GOFLAGS=-buildvcs=false` в фриз-сборке **ЗАПРЕЩЁН**.
⚠ Штамп снимается **на КАЖДУЮ попытку**, а не один раз: резюм может сменить бинарь под тем же прогоном —
сверяй по `run_attempts`, а не по тому, что лежит на PATH в конце.
**ПОРЯДОК ЖЁСТКИЙ, и он важен потому, что пре-рег обязан НАЗЫВАТЬ тот самый sha, который поедет:**
клон → записал sha и собрал бинари → написал пре-рег, НАЗЫВАЮЩИЙ этот sha → отдал мне → **я коммичу**
ты сверил, что заландженный пре-рег называет твой sha → и только теперь первый платный вызов.
**Что делать, если я молчу.** Канал может отсутствовать — это нормальный случай, а не авария. **Ждёшь
меня до конца своего хода, потом ОСТАНАВЛИВАЕШЬСЯ и пишешь отчёт** с готовым пре-регом и пометкой «жду
лендинга фриза и значения `TM_PLATFORM_ENGINE_KEYS_PATH`». ⛔ **Без обоих платная фаза не начинается ни при
каких обстоятельствах** — и это не формальность: пре-рег, не заландженный ДО денег, перестаёт быть
пре-регом.
**Что фриз НЕ запрещает:** потолки правятся **БЕЗ пере-фриза** — фриз-гейт смотрит на ПОКУПАЮЩИЙ файл, а
не на кассу. Меняется ЧТО покупается или чем судится — новый фриз; меняется СКОЛЬКО тратить — объявление в
отчёте. Правка собственного драйвера после фриза — тоже новый фриз.
### 4.3 Пре-регистрация — **главный рубеж пака, и он дороже самого прогона**
**ЗНАМЕНАТЕЛЬ ПРЕ-РЕГИСТРИРУЕТСЯ ДО ДЕНЕГ, иначе B ответит числом на другой вопрос.** B — ЕЩЁ ОДИН
одиночный прогон, и две точки распределением не станут. Настоящая ценность B не «вторая точка к 25.4 %», а
`request_log` ВСЕХ единиц прогона ⇒ **доля выброшенных первых попыток ПО ЕДИНИЦАМ, а не по вызовам.**
**Мера задана мной, делай РОВНО так** (я снял её на прогоне A своим прибором, числа ниже — твой базис
сравнения):
- **единица** = `(chapter, chunk_idx, stage, role)`. ⚠ **Единица ОХВАТЫВАЕТ попытки и охватывает их
намеренно** — номера попытки в ключе нет. И знай асимметрию: у стадии `terminology` все строки прогона A
несут `chapter = 0`, то есть `chunk_idx` там — индекс майнинг-батча, а не чанк главы. Одно слово
«единица» покрывает два разных предмета; называй, какой именно, в каждом числе.
- **знаменатель** = единицы, за которые заплачено хоть раз (`sum(cost_usd) > 0`). ⚠ На прогоне A этот
фильтр **не отсеял НИЧЕГО** (17 единиц всего и 17 платных) — значит его поведение ни разу не проверялось,
и на B единица из одних чекпойнтов может появиться впервые. Печатай ОБА счёта.
- **ЧИСЛИТЕЛЬ ПЕРВЫЙ — выброшенная покупка:** единицы, где есть хоть одна оплаченная и НЕ принятая попытка
(`ok = 0 AND cost_usd > 0`). **Прогон A: 4 из 17 = 23.5 %**, тогда как по ВЫЗОВАМ то же событие даёт
4 из 27 = **14.8 %**. Две меры расходятся в полтора раза — вот почему мера объявляется заранее.
-**ЧИСЛИТЕЛЬ ВТОРОЙ, и первая редакция этого промта его СЛЕПО ПРОПУСКАЛА** (нашёл опровергатель, я
пере-снял сам): **единицы, за ПРИНЯТЫЙ результат которых заплачено больше одного раза** (`ok = 1`,
ячейка куплена дважды). На прогоне A это вся стадия терминологии, пере-купленная на резюме:
**$0.028742 = 6.9 % цены книги**, и мера «выброшенная первая попытка» этого НЕ ВИДИТ вовсе — строки
`ok = 1`. ⚠ Черновик на том же резюме приехал бесплатно (6 строк `tm_hit = 1`), терминология — нет.
**Обе доли печатаются отдельно; складывать их в одно «потеряли N %» НЕЛЬЗЯ — это разные беды.**
**ДВА ПУТИ К ДЕНЕЖНОМУ ЧИСЛУ — и честно о том, чего их совпадение стоит.** Путь 1 — по колонке
`degraded`; путь 2 — по `ok = 0 AND cost_usd > 0`, колонки `degraded` не касаясь. У меня на A оба дали
ровно **$0.106472 = 25.4 %** цены книги. ⛔ **Но на A они совпадают ПО ПОСТРОЕНИЮ**, а не независимо:
`degraded` там принимает ненулевое значение ровно на тех же четырёх строках. ⇒ совпадение на B — слабая
улика, а вот РАСХОЖДЕНИЕ — сильная находка: оно означает, что классификатор и признак приёмки разъехались.
⚠ И не путай числа: **25.4 % — доля ДЕНЕГ, 23.5 % — доля ЕДИНИЦ.** Это разные величины, и подменять одну
другой в отчёте нельзя.
В пре-рег идут также: **ожидаемый исход КАЖДОГО шага, написанный ДО прогона** · sha256 исходника · sha256
ФАКТИЧЕСКОГО pipeline-YAML (платформа берёт файл ЦЕЛИКОМ, пер-полевого оверрайда нет ⇒ «прогон на c1» без
sha — утверждение без носителя) · список глав с `units_total` · грант, TTL выгрузки.
**ПРЕТРЕЙН — И ЭТО ТЕПЕРЬ НЕ ЗАПРЕТ, А ПРОЦЕДУРА (слово владельца 11.09 отменило прежнюю редакцию).**
`蛊真人` узнаётся моделями 4 из 5. Прежняя редакция этого промта на этом основании запрещала клеймы о
качестве вовсе — **владелец отменил запрет: он хочет увидеть качество на боевой книге.** Но опасность
никуда не делась и становится СИЛЬНЕЕ, когда читатель — большая модель: она способна «увидеть» в
переводе то, что помнит из претрейна, и не заметить того, чего в переводе нет. ⇒ процедура вместо
запрета: **каждая находка о качестве обязана нести ДВЕ цитаты рядом — фрагмент ОРИГИНАЛА и фрагмент
ПЕРЕВОДА.** Находка, которая не может показать место в исходнике, — не находка, а воспоминание.
### 4.4 Платная фаза
**Реши сам и аргументируй:** какую книгу и сколько глав брать. ⛔ **Глав немного — слово владельца.**
Мой приор, опровергается замером: те же главы 13 той же книги, что и в A. ⛔ **И вот ловушка, которая
стоила бы тебе ТРОЙНОГО бюджета, — первая редакция промта в неё попадала.** Файл
`books/gu-zhenren/coldrun-v16/guzhenren-ch1-10.gb18030.txt` — это **10 глав, 57838 байт**. Прогон A ел не
его, а **СРЕЗ в 17564 байта**, sha256 `ed870ba6065ce3eb…`, сохранённый как
`/home/ubuntu-26/tm-coldrun-a/material/guzhenren-ch1-3.gb18030.txt` (срез байт-в-байт равен первым 17564
байтам целого файла — проверено мною обеими sha). **Интейк главами резать НЕ умеет**: загрузишь целый файл —
купишь ×3.3 текста против объявленных ≈$0.42. ⇒ **режешь файл сам и кладёшь его sha в пре-рег.**
⭐ **Границу среза я пере-снял по возражению исполняющей сессии (байтовая граница в GB18030 могла разрубить
многобайтный символ) — она ЧИСТАЯ:** срез декодируется целиком, **9021 знак**, кончается на границе абзаца,
и несёт **ровно 3 заголовка**`第一节` · `第二节` · `第三节` (в целом файле их 10; контроль печатается
рядом). ⚠ **И ловушка имени, на которой я сам получил ложный ноль:** глава здесь размечена иероглифом
**节**, а не 章 — мой первый греп искал `第…章` и нашёл 0. Инвентарь разметки — `backend/internal/lang/data/cjk-section.txt`.
**ДВА ПОТОЛКА, а не один, и первая редакция называла только платформенный.** Холд платформы ≈$2.5 — и
**движковые потолки в `book.yaml`**: у прогона A стояло `book_usd: 1.25`, `day_usd: 2.50`. Прогон
остановится на ДВИЖКОВОМ потолке раньше, чем на платформенном, и «упёрся в потолок» будет про число,
которого ты не ждал. Назови в пре-реге ОБА и скажи, какой ставишь ты.
**A↔B СРАВНИМЫ НЕ ПО КНИГЕ — И ЭТО НАДО ЗАПИСАТЬ ОГРАНИЧЕНИЕМ, А НЕ ЗАМОЛЧАТЬ.** Между фризом прогона A
(`b0f5d89`) и сегодняшней головой — **107 коммитов и 52 файла движка** (+7678/85), включая `stagerun.go`,
`disposition.go`, `quality.go`, `httpllm.go`, `provider_openai.go`, `requestlog.go` и `models.yaml`, то есть
ровно денежный путь. **B меряет ДРУГОЙ движок.** Одинаковая книга делает сравнимыми ТЕКСТЫ, но не числа
денег. ⇒ в пре-рег: sha движка B, число коммитов между ним и A, и прямая строка «двойная оплата могла
измениться из-за наших правок, а не из-за провайдера».
**Приёмка каждого узла — по ТРЁМ независимым следам** (Postgres · диск и systemd · HTTP), не по
самоотчёту движка.
**Живость длинного прогона проверяется АКТИВНО:** не жди финального уведомления — раз в пару часов сверяй
рост числа вызовов и трат по леджеру ДВУМЯ чтениями. **Зависшая задача от идущей по сигналу завершения
неотличима, и у зависания нет цвета.**
**СЛЕДИ ЗА ЛОГАМИ И ЛОГИКОЙ ЖИВЬЁМ, а не только по итогу (слово владельца 11.09).** Прогон — это не
«запустил и жди файл»: смотри, что движок пишет, пока он это пишет. Каждое предупреждение, каждый флаг,
каждая регенерация, каждый незнакомый `finish_reason` — читаются ТОГДА, а не в разборе. Вопрос к каждому:
**согласуется ли поведение с тем, что, по твоему чтению кода, должно было произойти?** Расхождение логики и
лога — находка, даже если прогон кончился успешно. Веди это списком по ходу: «время · что увидел · чего
ожидал · что это значит».
**Деньги: ждём ≈$0.42, кап ≈$2.5 холда.** Упёрся в потолок — **СТОП и пинг мне**, не режь молча и не
проси подъёма «по проекции»: прогнозный запрос оставляет ложный след «владелец согласился на пере-оплату»,
которой не было.
### 4.5 ⭐ ВЫЧИТКА ВСЕГО ПЕРЕВОДА — отдельным опус-агентом с МИЛЛИОННЫМ контекстом
**Это заказ владельца дословно, и это главная качественная работа пака.**
**Делай РОВНО так в части формы:**
1. Подними **отдельного агента на opus** (модель задавай ЯВНО) и **загрузи ему в контекст ЦЕЛИКОМ**:
**(а)** весь получившийся перевод и **(б)** весь исходный китайский текст тех же глав.
**Контекст НЕ ЭКОНОМИТЬ** — слово владельца: «пусть не экономит контекст, ему должно на всё хватить».
Никаких выжимок, никаких «первых N абзацев», никакого пересказа вместо текста. Режешь текст — режешь
находки, и молча.
2. Его мандат — **искать МАЖОРЫ, которые жёстко критуют по тексту и его качеству**, в ПОРЯДКЕ ПРИОРИТЕТА
владельца (он назвал его сам, и порядок несущий):
1. **КОНСИСТЕНТНОСТЬ банка памяти и терминов** — один и тот же термин/имя/титул, приехавший в разных
местах по-разному; термин, разошедшийся с банком; забытая форма.
2. **ОТСУТСТВИЕ ВЫДУМОК** — текст, которого в оригинале НЕТ: дописанные предложения, пояснения от себя,
«додуманные» связки, исчезнувшие куски (пропуск — та же выдумка, только с другим знаком).
3. **ХУДОЖЕСТВЕННОСТЬ И ЧИТАЕМОСТЬ** получившегося русского текста.
4. **ОТСУТСТВИЕ TRANSLATIONESE** — кальки, порядок слов оригинала, «китайский синтаксис русскими словами».
3.**Форма КАЖДОЙ находки — две цитаты рядом:** фрагмент ОРИГИНАЛА и фрагмент ПЕРЕВОДА, плюс глава/юнит,
плюс какой из четырёх приоритетов нарушен, плюс severity. **Находка без цитаты из исходника не
принимается** — см. абзац о претрейне выше: модель знает эту книгу и способна «вспомнить» то, чего в
нашем переводе нет.
4. **Решаешь сам и аргументируешь:** сколько таких читателей поднять и как разделить между ними работу
(один на всё · по приоритетам · по главам с перекрытием). Мой приор, опровергается доводом: **один агент
на ВЕСЬ текст сразу** — консистентность банка по построению не видна тому, кто читает главу отдельно.
Если делишь — объясни, чем компенсируешь потерю сквозного взгляда.
**И назови ОТРИЦАТЕЛЬНЫЙ результат тоже:** «выдумок не нашёл» — это результат, но только рядом с
контрольной величиной («прочитано столько-то знаков перевода против стольких-то знаков оригинала»).
**ДОКАЗАТЕЛЬСТВО ДОЧИТЫВАНИЯ — обязательно, дописано релеем 11.09 по возражению исполняющей сессии.**
Инструменты чтения РЕЖУТ длинные файлы молча, и тогда «выдумок не нашёл» становится утверждением о
НЕПРОЧИТАННОМ, неотличимым от находки. ⇒ читатель обязан напечатать: **знаков оригинала · знаков перевода ·
ПОСЛЕДНЕЕ предложение оригинала и ПОСЛЕДНЕЕ предложение перевода**. Не напечатал — его отчёт не принимается.
**Контроль для среза глав 13: 9021 знак оригинала** (снято мною).
### 4.6 ⭐ СВЕРКА ПРИБОРОВ С ВЫЧИТКОЙ — ради этого прогон и стоит своих денег
У движка есть СВОИ приборы качества: детерминированные $0-гейты, отчёт качества, счёт расхождения форм
(`I1`/`I2`), флаги и колонка `degraded`, банк памяти и пост-проверка. **Все они утверждают что-то о тексте.
Твоя работа — спросить, совпадает ли их мнение с тем, что увидел читатель.**
Построй таблицу 2×2 и заполни её именами находок:
| | **читатель нашёл** | **читатель не нашёл** |
|---|---|---|
| **прибор сказал** | подтверждение | ложная тревога прибора |
| **прибор смолчал** | ⛔ **СЛЕПАЯ ПОВЕРХНОСТЬ** | согласие |
⛔ **Интересны ДВЕ клетки, и левая нижняя — самая дорогая: это дефект, который сегодня уезжает читателю
молча.** Каждую такую назови отдельно: что именно прошло мимо, какой прибор обязан был это поймать, и
почему не поймал — нет механизма, есть но выключен, есть но смотрит не туда.
### 4.7 КОД, КОТОРЫЙ ОТРАБОТАЛ — читать с оглядкой на получившийся текст
**Не ревью движка вообще, а ревью ТОГО ПУТИ, который реально исполнился на этой книге.** Порядок обратный
обычному: сначала находка в тексте, потом код, который её пропустил.
Назови: какие стадии и гейты реально бежали · какие не бежали и почему · где в коде живёт механизм,
отвечающий за каждый найденный мажор. ⚠ Пример связи, которую ждут: читатель нашёл разъехавшийся термин ⇒
читается путь инъекции банка и пост-проверка ⇒ ответ «механизма нет» / «механизм есть, но решает по
единице, которой в продукте нет» / «механизм есть и промолчал вот здесь».
**Кода НЕ ПРАВИТЬ** (§2). Находка = пинг мне со строками.
### 4.8 ДЁРГАТЬ API ПЛАТФОРМЫ ПО-РАЗНОМУ — не один счастливый путь
Слово владельца: «пусть подёргает по-разному апишку через платформу». Один проход по счастливому пути
доказывает, что дверь открывается, и ничего не говорит о том, что за ней.
**ГДЕ ЭТО ДЕЛАТЬ — исправлено релеем 11.09 по возражению исполняющей сессии, и возражение верное.**
Дёрганье API на ИЗМЕРЯЕМОМ прогоне САМО ПРОИЗВОДИТ число, которое потом предъявляется как свойство движка:
резюм и второй старт — ровно то, что числитель-2 §4.3 (пере-покупка ПРИНЯТЫХ единиц) и меряет. ⇒
**делай РОВНО так:**
- **ВСЕ формы дёрганья — на $0-РУКЕ** (стенд с заглушкой): API там тот же самый, провайдер подменён, и
стоит это НОЛЬ. Никаких оснований экономить на числе форм.
- **В ИЗМЕРЯЕМОМ платном прогоне — РОВНО ОДИН резюм, объявленный в пре-реге.** У прогона A резюм БЫЛ
(две попытки прогона в леджере, терминология пере-куплена на второй), поэтому один резюм не портит
сравнение, а СОХРАНЯЕТ его: без него A и B меряли бы разное.
**Решаешь сам, что дёргать; мой приор — не менее шести РАЗНЫХ форм:** заказ в главах против заказа в
ЗНАКАХ (это разные ветки контракта и разные счётчики) · повтор того же запроса (идемпотентность) · резюм ·
старт второго прогона при идущем первом · выгрузка во ВСЕХ смонтированных форматах ·
узел подписи банка · отказные пути (несуществующая книга · нехватка баланса · курсор с мусором).
**Ожидаемый исход КАЖДОГО дёрганья пишется ДО него.** Иначе это не проверка, а экскурсия: любой ответ
двери задним числом выглядит правильным.
### 4.9 Фаза 3 — $0 после прогона. **Состав задан РОВНО так; чем именно предъявлять — решаешь сам**
Прибор консистентности по ОТГРУЖЕННОМУ тексту существует и строить его НЕ НАДО — он живёт в
`backend/internal/pipeline/bookconsistency.go` и достаётся как `QualityReport().Consistency`; печатает его
`backend/cmd/tmctl/render.go`. Снимай `I1`/`I2` **с печатаемыми знаменателями** (число без знаменателя в
этом паке не принимается).
Дальше: выгрузка · структурное чтение EPUB (zip + `container.xml` + `nav`; `epubcheck` на хосте нет, и это
законно) · **обе новые миграции предъявлены ДАННЫМИ**: `wave_selection` несёт строки ОБЕИХ волн (они
выбирают над РАЗНЫМИ банками — черновик над базовым с исключёнными намайненными, редактор над обогащённым),
а `reasoning_in_completion` предъявляется **ТРЁХЗНАЧНО**: сколько строк `NULL`, сколько `0`, сколько `>0`.
Не гонись за «не нулём»: `0` здесь — законный измеренный ответ, и отличать его от `NULL` — весь смысл
миграции.
### 4.10 ⭐ НАХОДКИ ВНЕ ЗАКАЗА — и это отдельный заказ владельца, а не приятный побочный эффект
Слово владельца 11.09: **«важно, чтоб она находила проблемы даже там, где мы не ждём»**. Поэтому здесь стоит
вопрос, у которого НЕТ заранее известного ответа, и он задаётся в КАЖДОЙ фазе, а не один раз в конце:
> ⛔ **Что в этом прогоне стоит дороже, работает хуже или ведёт себя страннее, чем должно бы, — и о чём
> НИКТО не спрашивал?**
**Ради чего вопрос заведён — реальный случай, и он стоил четверти цены книги.** Удорожание из-за падающих
запросов НА НАШИХ СОБСТВЕННЫХ настройках никто не заказывал искать: просто кто-то посмотрел в леджер без
гипотезы и увидел, что 25.4 % денег ушло в выброшенные первые попытки. Ни один тест не был красным, ни один
гейт не сработал, в отчёте стояла зелень. **Находка такого рода ценнее исполнения заказа**, потому что
заказанное найдут и без тебя.
**Что докладывать (слово владельца, дословно по составу):** **криты · мажоры · регрессии · баги · жёсткие
точки улучшения.** Каждое — **АРГУМЕНТИРОВАННО**: что видел · чем предъявлено (`file:line`, строка лога,
запрос и ответ, число из леджера) · почему это именно крит/мажор, а не вкусовщина · чего это стоит в
деньгах, тексте или доверии читателя.
**И следи за КОДОМ, а не только за выдачей.** Ты читаешь логи и результаты движка — значит у тебя в руках
единственная в проекте позиция, с которой видно и поведение, и его причину сразу. Увидел странность в
выдаче — иди в код, который её произвёл, и назови место. Увидел в коде путь, который на этой книге НЕ
исполнился, хотя должен был, — это тоже находка. ⛔ **Кода не править** (§2): находка = пинг мне со строками.
## 5. Где этот пак мягкий — четыре места, назвал я, веер выбираешь ты
Мандат самопроверки — **исполнением**, субагенты разрешены явно. Направление:
1. **Сборщик, который молчит, и сборщик, который прочитал ноль, — неотличимы.** Засей в стендовую БД
заведомо ненулевой банк и заведомо удержанный юнит и потребуй, чтобы сборщики стали КРАСНЫМИ. Не
покрасневший на подсадке сборщик в платную фазу НЕ ДОПУСКАЕТСЯ.
2. **Дым меняет ТРИ фактора разом** (провайдер + книга + база) ⇒ при провале платной руки он не разделяет
причину. Где дёшево — меняй по одному.
3. **Отрицательный замер обязан доказать, что спросил существующее.** Рядом с каждым нулём ПЕЧАТАЙ
контрольную величину. И помни две ловушки среды: `grep` здесь — обёртка над ugrep, чтит `.gitignore` и
**не видит `books/`**; `sqlite3` как CLI на машине НЕТ — только `python3` + `mode=ro`.
4. **Число, выведенное из соседнего поля, не краснеет нигде.** Если в строке есть поле, прямо отвечающее на
вопрос (номер попытки живёт в `trace_id`), любой ВЫВОД ответа — по времени, по порядку, по соседству —
уже дефект метода, даже когда сходится.
### ⭐ Адверсариальная стойка — то, чем оркестратор судит чужую работу; суди ею свою
Владелец просил передать это прямо. Ниже не правила пака, а СПОСОБ СМОТРЕТЬ, и он ловит то, чего не ловят
ни тесты, ни второй круг:
-**Автор и ревьюер — разные роли, даже когда это один ты.** Перечтение собственной работы рубежом НЕ
считается. Поднимай отдельного читателя, которому НАЗВАНО, где мягко, и не показывай ему свои выводы
раньше, чем он прочтёт предмет.
-**Спрашивай у аномалии, о ЧЁМ она — о предмете или о твоём приборе.** Вмешательство в замер производит
ложные улики, похожие на находки: у прошлой смены запись «непонятный исход» оказалась здоровой, а сломан
был цикл того, кто мерил.
-**Верный результат при неверном методе не краснеет нигде.** «Сошлось» — не доказательство: сходятся и
по счастливому порядку строк. Спрашивай «чем это НАЗВАНО в самой строке?» прежде, чем вычислять.
-**«Не воспроизвелось» — не «недостижимо».** Верный прогон при неверной гипотезе неотличим от «дефекта
нет». Назови ЦЕПЬ звеньев от входа к следствию и проверь каждое, прежде чем писать «не воспроизводится».
-**Утверждение о молчании вакуумно.** «Ошибки не было», «лишнего не купили», «предупреждений нет» —
проверяемо только рядом с фикстурой или контролем, где это ОБЯЗАНО было прозвучать.
-**Заимствованное число проверяется не на существование, а на ТУ ЛИ КЛЕТКУ.** Та же роль, та же модель,
та же стадия, тот же режим? Прошлый пак чуть не включил денежный механизм по замеру из чужой роли.
-**Зелёная батарея плюс полный каталог мутаций сходимостью НЕ являются.** Три смены подряд направленный
второй читатель находил 6, 7 и 9 настоящих дефектов при полной зелени.
-**И главный вопрос отчёта — не «что не получилось», а «что ты знаешь и не сказала».** На прямой вопрос
сессия однажды назвала ДВЕНАДЦАТЬ слепых поверхностей своего прибора вместо одной, и две из двенадцати
не прозвучали бы без вопроса: одну она видела в собственном выводе и прошла мимо, вторую знала из своего
же комментария в коде.
## 6. Предметные оси ревью
Выбери 13 и назови какие: **деньги под гонкой** · **граница «холодности»** (что в прогоне перестало быть
первым разом) · **провенанс каждого числа** (чем снято, каким прибором, на какой клетке).
## 7. Записка-план ДО работы
Перед первой командой — записка: фазы, что чем предъявляется, где ждёшь осечки. Комплектность против
заказа сверяй механически, а не памятью.
## 8. Заявление = команда
Каждое число и каждая категорика отчёта идут **с командой, которой получены**. Приёмка пере-снимает.
## 9. Эхо-протокол старта
**Первым действием** — ≤10 строк СВОИМИ словами: что понял · что считаешь опасным · что считаешь неверным.
Дословный пересказ подтверждает канал, но не понимание, и ошибку промта повторяет вместе с ним.
**Адрес бери из `/tmp/textmachine-channel`** — блок с `role=оркестратор`, — а не из этого промта: имя сессии
не переживает рестарт окружения, и вписанное сюда имя однажды окажется мёртвым. Перед отправкой сверься с
`ListAgents`: файл переживает смерть сессии, а `ListAgents` — нет.
## 10. Что НЕ удалось — обязательная секция отчёта
И вторая её половина, без которой она вырождена: **«где прибор слеп и я это знаю»** — что невидимо · почему
не чинил · чем закрывается. ⭐ Формулировка прошлой смены, идущая нормой: **«в коде названо, в отчёте нет —
значит для следующей смены НЕ названо».**
**И ОТДЕЛЬНОЙ секцией — находки вне заказа (§4.10):** криты · мажоры · регрессии · баги · жёсткие точки
улучшения, каждая аргументированно и с носителем. ⚠ Эта секция обязана быть непустой ИЛИ нести строку
«искал вот так, не нашёл» с перечислением того, где искал: «находок нет» и «не смотрел» в отчёте выглядят
одинаково.
## 11. Канал вопросов и право отказаться
Конфликт промта с кодом или доками — **пинг мне, не интерпретация**. И у тебя есть право сказать «этого
делать не надо» с аргументом: прошлые три сессии возражали, и в двух случаях правы были они.
### ⭐ Старший коллега — Fable 5. **Разрешён ОДИН на сессию (слово владельца 11.09)**
У тебя есть право поднять **одного** агента на модели **`fable`** и держать его как СОВЕТЧИКА при сомнениях —
тем же способом, каким это делает оркестратор. Владелец разрешил ровно одного на сессию.
**Как с ним работать, и форма здесь важнее числа:**
- **Поднимаешь ОДНОГО и держишь его весь пак.** Ценность этого агента — в НАКОПЛЕННОМ контексте смены:
он помнит, что ты уже решил и почему. ⛔ **Второго не поднимай** — вопросы ДОСЫЛАЮТСЯ первому (`SendMessage`
по его id), а не адресуются свежему. Свежий агент вместо накопленного — потеря именно того, ради чего он
заведён.
- **Модель задавай ЯВНО** (`model: "fable"`), иначе она унаследуется и ты не будешь знать, что у тебя
работает.
- **О чём его спрашивать:** сомнение в решении · развилка, где оба пути выглядят законными · «не заказываю
ли я уже построенное» · «не противоречит ли это ратифицированному» · спорная формулировка в отчёте.
**Спрашивай С КОНТЕКСТОМ и со СВОЕЙ рекомендацией** — вопрос без твоего варианта ответа даёт совет ни
о чём.
-**Он советчик, а не источник истины: его ответ проверяется деревом.** У оркестратора он за смену
ошибся дважды на фактах (называл платный прогон бесплатным, объявлял ряд новым предметом) — и оба раза
его ВЫВОД оставался верным. Проверяй посылки, принимай выводы по существу.
- **Что он дал оркестратору за эту смену** — для калибровки, чего от него ждать: поймал, что я собирался
завести новый ряд бэклога под предмет, у которого ряд уже был; развернул порядок паков доводом, который
я не назвал; и назвал условие, без которого два прогона стали бы несравнимы.
## 12. Прямой канал
Механизм — `CLAUDE.md` §«Связь между сессиями». Впиши свой блок в `/tmp/textmachine-channel` ПЕРВЫМ
действием. Нужной роли нет ⇒ канала нет, и это нормально: НЕ опрашивай сессии подряд.
## 13. Критерий завершённости
У каждого пункта заказа — исход (сделано · не делаю с доводом · пинг) · круги СОШЛИСЬ (последний не дал
НОВЫХ находок) · числа сняты ПОСЛЕ последней правки · всё живое в ДЕРЕВЕ или у меня, а не в письме ·
явное **«работа завершена, править не планирую»**. Без последнего пак считается идущим.