textmachine/docs/BACKEND_EFFORT_HANDLE_SESSION_PROMPT.md

123 lines
46 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.

# Промт бэкенд-сессии: РУЧКА ЭФФОРТА + ЭКРАН ЦЕЛЕВОГО ЯЗЫКА + ДОБИВКА ХОЛОДНОГО ПРОГОНА (строки 104 · 46 · 16)
**Выдан 02.08.2026, оркестратор №10; решение владельца по развилке строки 74 (D39.87): вариант (б) — явный `reasoning_effort: "low"` для черновика ПЛЮС правка Go, дающая ручку тем ролям, у которых её нет.** Зона: `backend/` + стенд `~/books/gu-zhenren/coldrun-b/` + свой отчёт в `docs/archive/reports/`. **НЕ коммитить** — лендит оркестратор. **Первый деливерабл — эхо-блок ≤10 строк** (первым сообщением ДО работы: как понял скоуп / три работы и их СТОП-точки / потолки денег / что вне скоупа; подтверждения не жди — работай до СТОП-условий).
**Деньги (жёстко):** работа A = **$0**. Работа B = **$0**. Проба банка на `low` (§3.4) ≤ **$0.10**. Работа C (прогон) ≤ **$5.00** суммарно. ⚠ **$5 — ВНЕШНИЙ предохранитель, и он НЕ первый.** Внутри прогона стоят два суб-бюджета, которые связывают раньше и **гаснут МОЛЧА, без ошибки**: `gates.terminology.budget_usd: 0.05` (≈4 прохода терминолога — WARN `budget would be exceeded by the next batch; the remaining terms are left unconsolidated` + `break`) и `escalation.budget_usd: 0.10` (≈11 хопов — WARN `escalation hop denied by a USD ceiling; keeping the primary flag`, деградация без падения; ровно это усекло наблюдение эскалаций на прошлой пробе). Оба — в `~/books/gu-zhenren/coldrun-b/pipeline.yaml`. **Не спутай молчащий суб-бюджет с отказом модели** — иначе поставишь ложный СТОП; и не подними их молча — правку суб-бюджета объяви в отчёте с причиной. Потолки продублированы в `book.yaml ceilings`: `book_usd: 5.00` И `day_usd: 5.00` (ключа `budget_usd` не существует — строгий декодер упадёт; ⚠ `day_usd` считается по КАЛЕНДАРНОМУ дню UTC и по ЭТОЙ БД, `store/ledger.go:39,47` — через полночь ось обнуляется, несущий предохранитель = `book_usd`). Превышение любого потолка = СТОП+релей. Проба §3.4 живёт в СВОЁМ проекте (свой `book_id`, свой `project_db`) ⇒ её леджер отдельный и в потолок $5 работы C **не входит** — $0.10 сверх; в отчёте выведи ТРИ числа: проба · прогон C · сумма пака. В отчёте деньги — **двумя независимыми путями** (леджер И собственный пересчёт провайдерского `usage` по вендор-ценам); помни: **леджер = НИЖНЯЯ граница** (строка 78: 2xx с битым декодом списывается и в `request_log` не попадает — на прошлой пробе это было $0.0151 неизвестности на 17 оплаченных вызовов).
---
## §0. Рамка и ГЛАВНЫЙ мандат этого пака
**Слово владельца дословно: «элегантно встроить в текущее решение по коду. Чтоб было чисто».** Это не украшение — это приёмочный критерий. Перевожу его в проверяемые вопросы, на каждый из которых отчёт обязан ответить:
1. **«Это новый механизм или использование существующего?»** Правильный ответ — второй. Поле `Stage.Reasoning` существует с самого начала (`config/pipeline.go:208`), проброс на провод существует (`stagerun.go``llm/capability.go`), капабилити-слой различает провайдеров данными. Ничего из этого изобретать заново НЕ надо.
2. **«Появится завтра ЧЕТВЁРТАЯ синтетическая стадия — она унаследует ручку сама или её опять забудут?»** Это главный вопрос пака. Корень дефекта — не «забыли ключ», а **рукописные литералы `config.Stage`, каждый из которых молча теряет поля родителя**: в шиппинг-коде их ровно три (`terminologist.go:515`обе бэнк-роли; `terminologist.go:568` — не-проводная, из неё читается только `Name`; `internal/pipeline/repair.go:384` — ⚠ в дереве ДВА `repair.go`, нужен пакет pipeline; здесь родительская стадия РЕАЛЬНАЯ и несёт выбор владельца, который просто теряется), **плюс ЧЕТВЁРТЫЙ в live-риге** (`internal/pipeline/live_reprobe_test.go`), и он не декоративный — см. §3.4. Правка, чинящая места копипастой, дефект НЕ закрывает — она его тиражирует. Перед стройкой пере-проверь список сам: `grep -rn 'config\.Stage{' --include='*.go' backend/`.
3. **«Заработает ли пара, которой в репо ещё НЕТ, без правки Go?»** и **«вторая книга той же пары — без ложных флагов?»** — обязательны на каждый фикс, как всегда.
4. **«Не появилось ли второго способа сказать то же самое?»** Один шов, одно место решения, никаких параллельных путей.
**Дисциплина:** никаких хаков и ворэраундов; каждый фикс — поддерживаемое мультиязычное решение с тестом. **Архитектурная дыра → СТОП+пинг с диагнозом и вариантами, не чинить на месте.** Комментарии — 12 строки «почему», улики — в отчёт (норма владельца 26.07). **Самопроверка исполнением обязательна** (свой код + сформированные запросы + полученные результаты); **вывод на агрегате до вскрытия единиц запрещён** — перед любым «узел работает» открой хотя бы одну живую единицу с каждой стороны. Дифф `^func Test`**исполнением, не памятью**. В конце — адверсариальный проход author≠reviewer по своим правкам. **Канал вопросов:** непонятно / конфликт промта с кодом / замер бьёт по посылке → СТОП+пинг оркестратору через владельца, НЕ интерпретация. **Право сказать «этого делать не надо»** — есть, с аргументом в отчёте.
---
## §1. Онбординг (карта чтения, целиком доки НЕ читать)
1. `CLAUDE.md` (гардрейлы: `.env` НЕ читать · **DeepSeek thinking НЕ отключать** · аномалия провайдера → вендор-дока, не гадать) → CURRENT-STATE в `docs/PROGRESS.md` (строки **104**, **46**, **74**, **16**, **13б** — твои).
2. **`docs/architecture/05-decisions-log.md` — блоки D39.86 и D39.87 целиком** (приёмка ре-пробы со всеми числами и поправками; решение владельца по развилке + мандат «элегантно и чисто» как приёмочный критерий).
3. **`docs/experiments/00-provider-quirks.md`, секция `DeepSeek-V4-Flash-0731` целиком, особенно п.3а и п.10** — вендор-таблица маппинга эффорта и что именно замерено на боевой форме. Читать ПЕРЕД любым платным вызовом.
4. `docs/archive/reports/COLDRUN_B_DEBUG_2026-08-02.md`**ревю-шапка обязательна** (в ней поправки приёмки; тело отчёта местами точнее шапки не является).
5. `backend/README.md` (инварианты «ЛОМАТЬ НЕЛЬЗЯ») + `docs/architecture/12-go-style-notes.md` §0 (норматив общности).
---
## §2. Что УЖЕ установлено замером — не пере-открывать (приоры, опровергаются аргументом/замером)
- **Блокер — не эхо, а ПУСТЫЕ ответы.** На боевом `max_tokens=8496` 0 из 9 вызовов пригодны: размышление (1521 тыс. симв.) съедает общий бюджет, до ответа модель не доходит. Удвоение потолка НЕ помогает (4/5 пустых при 16992) — вариант «поднять флор» фальсифицирован и закрыт.
- **Причина — вендор-дефолт эффорта.** Дословно из `guides/thinking_mode`: *«Thinking mode is enabled by default, with the default effort being high»*. Маппинг: у `deepseek-v4-flash` `low→low`, `high→high`, `xhigh→high`, `max→max`; **у `deepseek-v4-pro` `low→high`** — то есть на pro ручка эффорта не работает вовсе. Сноска вендора: *«We will update the actual mapped effort of `deepseek-v4-pro` in early August 2026»* — то есть прямо сейчас; при аномалиях у РЕДАКТОРА идти в вендор-доку, а не гадать.
- **`reasoning: "low"` на deepseek-стадии легален и гейт `echoMineViolation` НЕ трогает** (гейт инспектирует МЕХАНИЗМ `capabilities.reasoning.control`, не значение; у deepseek `ReasoningNone`, где `off`/`""` — осознанный no-op, а `low` уходит на провод). Проверено проводом: 22 тела с `reasoning_effort:"low"`, ноль ключей `thinking`, ни одного пустого `reasoning_content` (отчёт: 24/24 разобранных; пере-счёт приёмки по сырому логу — 27/27, разница только в знаменателе) ⇒ **гардрейл «thinking не выключать» не нарушается.**
- **Замеры варианта (б) на 20 чанках:** 20/22 вызовов `finish=stop`, 18/20 чанков отгружено, волна **$0.028195** all-in ($0.019489 только flash), латентность 944 с. Цена: эхо 3/20 · **1 черновик из 18 пришёл ЦЕЛИКОМ НА АНГЛИЙСКОМ и прошёл как `ok`** (это и есть работа B).
- **Ре-ген лечит СТОХАСТИКОЙ, а не бюджетом:** единственный выживший вызов пробы A уложился в 8285 токенов при базовом потолке 8496 и выданных 16992 ⇒ доливать бюджет бессмысленно.
- **Терминолог на 0731 сломан:** 4 батча из 5 `length` с `completion=8000` ровно и пустым телом, банк консолидировал 1 терм из 81 (было 21/21 `stop` до смены весов). **При thinking-off он был здоров 5/5 — но thinking-off это разворот гардрейла, мы его НЕ делаем.**
-**Терминолог на `low` НЕ ЗАМЕРЕН НИКЕМ.** Это открытый вопрос твоей §3.4, а не известный факт. Формулировку квирков п.10 «терминолог … ручкой НЕ лечится» читай вместе со следующей фразой того же буллета — «бэнк-роли не имеют ручки эффорта вовсе ⇒ всегда едут вендор-дефолтом `high`»: там сказано «ручки НЕТ», а не «`low` пробовали». Противоречия с этим промтом нет.
---
## §3. Работа A — ручка эффорта для ролей, у которых её нет (строка 104). $0 кода + ≤$0.10 пробы
**Цель:** выбор владельца «сколько думать» должен доезжать до КАЖДОГО платного вызова движка, а не только до стадий книги. Сегодня он доезжает до `draft`/`edit` и теряется у бэнк-ролей (терминолог, классификатор) и у repair.
**Делай РОВНО так (ратифицировано):**
- Значение приходит из **ДАННЫХ** (конфиг), Go не ветвится ни по провайдеру, ни по паре, ни по книге.
- Ручка ран-скоупная, **не пар-данные и не книжный канон** — в пар-пак и в `book.yaml` она не заходит.
- Правка обязана накрыть **все три** места разом и так, чтобы **четвёртое такое же место не могло появиться молча** (см. §0 п.2).
- `repair.go:384`: родительская стадия `st` РЕАЛЬНАЯ и уже несёт выбор владельца — здесь речь не о новом ключе, а о **прекращении его потери**. Доккоммент `repair.go:378-379` («no reasoning setting, so the provider default holds — on DeepSeek that means thinking stays ON and the echo mine is not armed») декларирует посылку, **которую замер фальсифицировал** — он обязан уехать вместе с правкой.
- **Ловушка хеш-оси, закрыть ТЕМ ЖЕ диффом — ОБЕ расщеплённые пары, не одна:** `bankCheckpointExists` строит `Request` БЕЗ `Temperature`/`Reasoning` (`terminologist.go:498-501`), а `runAttempt`С ними (`stagerun.go:403-406`); `Reasoning` входит в `RequestHash` (`internal/pipeline/render.go:279,308` — в дереве ДВА `render.go`, нужен пакет pipeline). **ТА ЖЕ пара есть на пути repair:** `repairCheckpointExists` (`repair.go:350`) собирает `Request` тем же обрезанным способом, и её доккоммент (`repair.go:346-349`) прямо декларирует «mirrors runRepairAttempt's request identity exactly … the two must address the same checkpoint» — правка `:384` без `:350` этот инвариант ФАЛЬСИФИЦИРУЕТ. Сегодня оси совпадают только потому, что значения нулевые. **⚠ Расходятся они в ОБЕ стороны, и вторая — денежная, она и опаснее:** (i) `paid` ложно-**false** — на резюме уже оплаченного прохода бюджетный пред-чек засчитает оценку заново и может оборвать проход до первого вызова; (ii) `paid` ложно-**true** — на стенде, где уже лежат бэнк-чекпойнты, записанные при ПУСТОМ `Reasoning` (а такой стенд у тебя есть — `reprobe/optB`), проба адресует старый хеш и вернёт «оплачено», из-за чего гейт суб-бюджета (`terminologist.go:583`, условие `!paid && …`) не выполнится ВООБЩЕ, а `runAttempt` посчитает новый хеш, чекпойнта не найдёт и уйдёт в свежий ПЛАТНЫЙ вызов **мимо `gates.terminology.budget_usd`** — прикрывать останется только книжный потолок. Оба направления закрываются ОДНИМ диффом и оба обязаны иметь тест: (1) резюм оплаченного пасса бюджетом не режется; (2) свежий пасс на стенде со старыми чекпойнтами гейт бюджета ПРОХОДИТ, а не обходит. Пере-проверь сам, нет ли третьей такой пары: `grep -rn 'RequestHash(Request{' --include='*.go' backend/`.
- **Доккомменты эхо-гейта, тем же диффом (обязанность D39.86 п.2 / D39.87):** `config/models.go:440-448` и `llm/capability.go:52-58` утверждают инвариант «echo-prone провайдер ОБЯЗАН держать thinking на дефолте провайдера». После санкции (б) это неверно: тот же провод легален через `stages[].reasoning` и запрещён через `model.extra_body` (`thinkingControlExtraKeys` содержит сам ключ `reasoning_effort` при любом значении). Гардрейл «thinking не выключать» цел — неполна ДОКУМЕНТАЦИЯ защиты, и следующая сессия прочитает её как полную. Сам гейт **не ослаблять**.
**Реши сам и аргументируй в отчёте:**
- **Форма шва.** Мой приор (опровергается аргументом): корень — рукописные литералы `config.Stage`, теряющие поля; значит лечение — ОДИН шов деривации синтетической стадии, где по каждому полю принято ЯВНОЕ решение, и все три места идут через него. Если найдёшь форму чище — бери свою, но ответь §0 п.2 явно.
- **Гранулярность ручки внутри блока банка.** `gates.terminology` обслуживает ДВЕ роли и допускает ДВЕ РАЗНЫЕ модели (`Model` для терминолога и `ClassifyModel`/`ClassifierModel()` для классификатора). Одна ручка на обе или две — решить и обосновать: у ролей разная форма задачи (длинный рендер против короткой классификации с закрытым словарём), и замер показал, что в стену упирается именно длинная (классификатор при `high` уложился в 1323 токена вывода). Не размножай ключи без нужды, но и не склеивай молча.
- **`Temperature` тем же диффом.** Та же конструкция роняет и её (на провод уходит `temperature: 0`). Сегодня ущерба нет — DeepSeek в thinking-режиме температуру молча игнорирует (quirks) — но при смене модели бэнк-роли поедут на нуле без единой строки конфига, это описывающей. Решить осознанно: пробрасывать, или явно зафиксировать нуль комментарием «почему так правильно».
- **Мульти-провайдерность ручки.** У `control = extra_body_disable` (glm-5/5.1) ПУСТОЙ эффорт означает не «вендор-дефолт», а `thinking:{type:disabled}` — то есть thinking ВЫКЛЮЧЕН, зона эха. Конфиг это допускает: `gates.terminology.model` не ограничен семейством. Дизайн обязан закрывать оба режима осознанно, иначе ручка починит только `ReasoningNone`-семейство и оставит на glm тихий thinking-off.
- **Наш enum против вендорского.** Валидация (`config/pipeline.go:954-957`) принимает `"" | off | low | medium | high` — это НАШ набор, он per-стадийный и **слеп к провайдеру**. У DeepSeek документированы `low/high/max` (+`xhigh` в таблице маппинга): `medium` на deepseek — неопределённое поведение, а `max`/`xhigh` нашим конфигом недостижимы вовсе. Нужно ли валидировать значение против капабилити РЕЗОЛВНУТОЙ модели — твой дизайн-вызов; если да, это касается и новой ручки. Аргументируй решение либо явный отказ.
**§3.4. Проба банка на `low` (≤$0.10, ОБЯЗАТЕЛЬНА перед работой C).** Терминолог на `low` не мерил никто — это открытый вопрос, а не известный факт.
- **⚠ Хост пробы: НЕ `coldrun-a`.** Тот проект — ЗАМОРОЖЕННЫЙ ЭТАЛОН (банк-стоп, подписные сайдкары), на нём держится сравнимость цены строки 16; писать в него ЗАПРЕЩЕНО, только читать. Отдельной команды «прогнать только бэнк-стадию» не существует (`--verify-bank` лишь ОСТАНАВЛИВАЕТ после банка), а майнинг-стоп идёт после ВСЕЙ черновой волны ⇒ проба «с нуля» купила бы ещё одну черновую волну и вышла за потолок.
- **Маршрут (приор, опровергается аргументом): СВОЙ проб-проект, а не резюм чужого.** Заведи `~/books/gu-zhenren/coldrun-b/reprobe/bank-low/` по образцу `reprobe/classify6/`: свой `book_id`, свой `project_db`, свои `ceilings: book_usd 0.10 / day_usd 0.10`, `source_file` = срез coldrun-a **на чтение**, черновая стадия на выбранном уровне. Смета: черновая волна ≈$0.02 + батчи банка ≈$0.01 ⇒ порядок $0.035, влезает с запасом. Так проба изолирована, её леджер отдельный, и провалиться она может не тронув ничего. ⚠ Альтернатива «резюмнуть `optB`, у которого волна уже куплена» дешевле, НО: у него остаток потолка $0.000534 при `book_usd: 0.04` (потрачено $0.039466) — потолок пришлось бы поднимать, а в его БД лежат СТАРЫЕ бэнк-чекпойнты, написанные при пустом `Reasoning`, то есть ровно материал для ложно-true из ловушки §3. Выберешь этот путь — аргументируй и закрой оба риска явно.
- **⚠ ДВА молчащих пути обрыва — критерий обязан их различать.** Пасс бэнк-роли деградирует БЕЗ ошибки и БЕЗ падения: (i) ролевой суб-бюджет `gates.terminology.budget_usd` (`terminologist.go:583-594`) — WARN `budget would be exceeded by the next batch; the remaining terms are left unconsolidated` + `break`; (ii) книжный потолок леджера — отказ резервации. В ОБОИХ случаях `consolidated` проседает ровно так же, как при сломанной модели. **Поэтому вердикт «`low` не работает» имеешь право вынести ТОЛЬКО предъявив, что ни один из двух путей не сработал:** прочитай `RoleSpentUSD` роли, остаток суб-бюджета и `spend` ДО и ПОСЛЕ, и приведи их в отчёте рядом с числом батчей. Иначе получится ложный СТОП, отменяющий работу C на пустом месте.
- **Ответь числами:** сколько батчей `finish=stop` из скольких, сколько термов консолидировано из скольких, `off_language`, цена, и вскрой СЫРОЕ ТЕЛО хотя бы одного батча (прошлая сессия этого не сделала и сама назвала это слабостью). **Критерий:** батчи должны пролезать. Не пролезают на `low` — это НОВАЯ развилка (другой уровень / другая модель бэнк-роли) ⇒ **СТОП+релей владельцу с числами, работу C не начинать.**
- **Классификатор** проверяется тем же заходом ригом 6/6 (`internal/pipeline/live_reprobe_test.go`, build-tag `live`, ~$0.0004). ⚠ **Риг несёт тот самый ЧЕТВЁРТЫЙ рукописный `config.Stage`, а `runBankAttempt` оставляет от переданной стадии только `Name`.** После твоего шва риг ОБЯЗАН ехать через тот же шов — иначе его зелёное 6/6 не доказывает про `low` ничего (ложно-зелёный результат за $0.0004). Это и есть живая проверка вопроса §0 п.2: если риг пришлось править руками отдельно — шов недостаточно элегантен.
---
## §4. Работа B — экран целевого языка (строка 46). $0
**Предмет:** на `low` наблюдался полный связный черновик **на английском**, прошедший гейт черновой стадии как `disposition=ok`. Корень: `classify` экранирует ИСХОДНУЮ письменность, пустоту, обрыв и отказ; «не исходный и не целевой» не ловит ни один предикат. Санитайзер поймал бы, но он стоит **только на последней стадии** (`chunkrun.go:52`, `isFinal`).
**Делай РОВНО так:**
- **Область — выход не-последней стадии КНИГИ, и только её.** ⚠ `isFinal=false` — это НЕ синоним «черновик»: тем же `runAttempt(..., isFinal=false)` идут бэнк-роли (`terminologist.go`) и repair, а их ответы **законно не в целевой письменности** (таблица терминов несёт исходные ханьские ключи; repair возвращает спан). Экран, повешенный на «любую не-последнюю стадию» буквально, даст 100% ложных флагов на терминологе. Скоуп задай так, чтобы это было невозможно, и покажи в отчёте, ЧЕМ именно он ограничен (роль? позиция в `Stages`? явный признак «это шиппинговая проза»?).
- Экран **данными, не Go-ветвлением**. ⚠ **Носитель `gates.terminology.target_script` НЕ годится** — я его предлагал в черновике промта и снимаю: ключа нет ни в одном шиппинговом конфиге репо, и живёт он под ЧУЖИМ гейтом (при выключенном терминологе не валидируется) ⇒ новый экран оказался бы инертен в проде. Носитель выбери сам и обоснуй: у движка уже есть дата-план целевой стороны (`internal/lang/data/`, `checks.Checkers` с `TargetActive()`/`TargetScriptNonLatin()`, `Book.TargetLang`) — приор в том, что ответ там, а не в новом ключе конфига. Ревью-вопрос: «пара, которой в репо нет, получает экран автоматически или требует правки Go?»
-**Это ВЕРДИКТ-ДВИГАЮЩЕЕ изменение.** Порядок: сначала дизайн и пинг с ним, **потом** код. В дизайне обязан назвать развилку, а не предрешать её: **(а)** предикат внутри `classify()` ⇒ бамп `classifierVersion` (`disposition.go:107-118`), который фолдится в снапшот (`snapshot.go:430`) ⇒ снапшот двигается у ВСЕХ книг и голден пере-захватывается; **(б)** отдельный гейт/флаг вне `classify()` ⇒ область фолда другая. Цена (а) на существующей книге — промах ВСЕХ чекпойнтов, `--resnapshot` + пере-покупка; на свежем стенде coldrun-b — ноль. Голден пере-захватывается ОДИН раз под ратифицированную смену поведения, и **маскированный структурный дифф обязан быть пуст**, если менялись только версии/хеши (голден-гард = инвариант №8, D23 п.1).
-**labels-фриз работа B двигать НЕ ДОЛЖНА вовсе** — она не трогает предикаты k2/k4a/k4b/k4_inverse/K6. Любое отклонение от чисел §7 = регресс, а не новый базлайн ⇒ СТОП+пинг, не пере-базлайнить.
**Реши сам и аргументируй:**
- **Вторая половина дыры, и она важнее для общности.** `detectLatinInsertion` (`checks/sanitizer.go:228`) внутри санитайзера НЕ обусловлен `TargetScriptNonLatin()`, хотя доккоммент `disposition.go:302-305` объявляет именно эту защиту (реальный гард стоит только на репэйр-пути — `quality.go:215`, `repair.go:208`). Следствие: как только будущая **латинописьменная** цель поедет задокументированным путём расширения (`data/target-<tgt>.txt` в go:embed), детектор сработает на ЧИСТОЙ целевой прозе и дропнет 100% чанков; а частичный файл цели невозможен — `CompileCheckers` паникует, то есть новая цель ОБЯЗАНА включить санитайзер. Ревью-вопрос владельца для →en/→de сегодня отвечается **НЕТ**. Кандидат лечения — тот же шов, что уже применён к репэйру. Твой вызов: чинить это здесь (по-моему — да, это одна работа с первой половиной) или выносить с носителем; ответь явно.
- Что делать с пойманным юнитом: флаг+скип, ре-ген, или эскалация. Помни: `empty`/`length` сегодня retryable, но НЕ escalatable (`disposition.go:142-149`), и ре-ген лечит стохастикой (§2).
---
## §5. Работа C — добивка холодного прогона (строка 16). ≤$5
**Идёт только после того, как работы A и B ЗАКРЫТЫ И ЗАМОРОЖЕНЫ** (код + пере-захваченный голден + зелёная батарея ВМЕСТЕ с ними) и §3.4 зелёная. Причина жёсткая: работа B двигает версию, фолдящуюся в снапшот ⇒ **любая правка B после первого платного вызова C промахивает ВСЕ чекпойнты книги** — смок C1 осиротеет и C2 перекупит его целиком, а «итоговая цена холодного старта» (единственный деливерабл строки 16) станет невыводимой. Работа B застряла на дизайн-пинге → работа C НЕ стартует, это легитимный исход захода.
Стенд `~/books/gu-zhenren/coldrun-b/` собран, $0-проверен и стоит нетронутым: свежая БД, **сид НЕ подключён** (прогон холодный), `pipeline.yaml` побайтно равен эталону coldrun-a кроме шапки, ключи проверены.
**Правка стенда, которую делаешь ТЫ (не подразумевается — делай явно):** в `~/books/gu-zhenren/coldrun-b/pipeline.yaml` черновая стадия сейчас несёт `reasoning: "off"`**это и есть тот no-op, который отправил прошлый прогон в стену 0/9.** Поставь туда выбранный владельцем уровень; комментарий строки («NO-OP у deepseek … эхо-мину НЕ вооружает») после правки неверен и должен уехать вместе с ней. Редакторскую стадию (`deepseek-v4-pro`) НЕ трогай: у pro эффорт не настраивается (§2), `"off"` там останется no-op'ом. Бэнк-роли — по итогам §3.4. Каждую правку стенда перечисли в отчёте построчно.
**Механика ступеней (не гадать):** майнинг-стоп идёт после ВСЕЙ черновой волны. **C1 смок** = `source_file` из 2 глав. **C2 добор** = ДОПИСАТЬ остальные главы в тот же `source_file`, ту же БД, тот же проект — **вторую БД НЕ создавать**, непрерывность автобанка и есть предмет отладки. ⚠ **ЛЕГИТИМНО при доборе:** рост автобанка двигает draft-снапшот; байт-неизменные юниты спасает re-pin (`repin.go`), а изменённые инъекции глав смока перекупятся ПОД порогом консента (`min($0.50, 5%)`, `rebill.go`) — это НЕ тихая перепокупка и НЕ дефект: посчитай, объясни, ложного СТОПа не делай.
**Предохранитель редакторской волны (узел без рычага).** Редактор стенда — `deepseek-v4-pro`, у которого по §2 ручка эффорта НЕ работает (`low→high`), а сама редакторская волна **не гонялась НИ РАЗУ** — ни до смены весов, ни после (coldrun-a закрылся на банк-стопе). То есть в C1 ты впервые запускаешь узел того же класса отказа, что убил черновик и терминолога, и рычага у тебя там нет. **Останови после ПЕРВЫХ 2 edit-юнитов и посмотри `finish_reason`, `completion_tokens` и непустоту `content` на каждом** прежде чем пускать волну целиком. Упор в потолок / пустое тело / `completion` ровно в лимит = тот же блокер ⇒ **СТОП+пинг с числами**, не подбор числа и не «поднимем флор».
- **C1.** Черновая волна → майнинг-стоп → терминолог → банк-пауза `--verify-bank` как ТВОЯ инспекционная точка (не подпись владельца — банк автономен, D39.77): вскрой банк (язык dst · экран целевой письменности · размеры · KWIC-санити · доля ⟨проверить⟩), вскрой ГЛАЗАМИ 23 черновика, сверь деньги двумя путями. Всё объяснимо → продолжай с НЕподписанным банком (пере-запуск `translate` без `--verify-bank`). Редакторская волна → гейты/чекеры → экспорт → вскрой 23 финала глазами.
- **C2.** Остальные ~8 глав тем же порядком (банк-пауза на твоё усмотрение, аргументируй).
- **Обязательные наблюдения per-узел:** счётчики волн/ретраев/эскалаций · эхо-события · упоры `max_tokens` · банкнота: кандидаты→банк и потери на пути · инъекция банка — **подтверждать байт-диффом СЫРОГО ТЕЛА: `LOG_LEVEL=debug LOG_LLM_BODIES=1 ./bin/tmctl translate …` (ТОЛЬКО вместе — при дефолтном `LOG_LEVEL` тела не пишутся вовсе, `main.go:66`), НЕ по `injected_ids`** (там только exact-хиты); ⚠ тела в логе УСЕЧЕНЫ (`obs/logging.go`) — на длинных ответах JSON не парсится, поэтому деньги считай из `request_log`, а не из лога · распределение `flag_reason` · wall-clock по стадиям и итогом · деньги нарастающим итогом двумя путями · **минимум ОДИН намеренный стоп+резюм посреди волны** · бэкапы F4 на каждом платном старте.
- **⚠ ОСИ ГОЛОСА — впервые получат данные (строка 13б).** Флаггер голоса живёт на ФИНАЛЬНОЙ волне (`waverun.go:392,512`), а редакторская волна не гонялась НИ РАЗУ — поэтому на coldrun-a оси показывают `flags=0 over attributed=0 of replies=0`, и это «не считалось», а не «чисто». Твой прогон — первый, где они реально отработают: **вынеси их числа со знаменателями отдельным пунктом отчёта**, это гейт решения владельца по `speech-cue.txt`. ⚠ Помни: поле `rules=` в строке `VOICE` говорит «гейт СКОНФИГУРИРОВАН» (читается из конфига времени отчёта, `quality.go:436`), а не «отработал» — честный дискриминатор это знаменатели.
**Прогноз цены — приор, чтобы ты узнал аномалию, а не просто упёрся в потолок** (опровергается замером; считай свой по ходу): черновая волна 20 чанков на `low` ≈ $0.02 · терминолог ≈ $0.010.02 · редакторская волна — **никогда не гонялась**, оценка из единственного наблюдённого вызова `deepseek-v4-pro` (9059 выходных токенов, $0.008706) на ~14 edit-юнитов ⇒ ≈$0.14. **Итого порядок $0.2 на 10 глав, потолок $5 = запас ×20.** Если фактическая цена уходит за ~$1 — это не «дорого», это СИГНАЛ: останавливайся и смотри, что покупаешь, а не жди потолка. ⚠ Редактор едет на вендор-дефолте `high` и придушить его нельзя (маппинг pro `low→high`), а вендор объявил смену маппинга pro на начало августа — если редакторские вызовы начнут упираться в потолок или возвращать пустое, это тот же класс, что блокер 74, и он требует СТОП+пинга, а не подбора числа.
- **Дебаг-дисциплина:** дефект узла → СТОП волны (чекпойнты держат) → диагноз до корня (аномалия провайдера → официальная вендор-дока, НЕ гадать) → фикс по норме §0 (мультиязычный, с тестом; батарея + голден зелёные ДО resume) → точечный redrive (пере-покупка видима и посчитана). **Заморозка прогона чекпойнтом ради доработки — ЛЕГИТИМНЫЙ исход, не провал.**
---
## §6. Чего НЕ делать
Сид НЕ заводить (прогон холодный) · судья Ф2 не гоняется · DC7-остаток НЕ мерить (строка 98 требует СИДОВОГО прогона — этот бессидовый) · **`backend/configs/models.yaml`: блоки `capabilities` (включая `reasoning`, `min_max_tokens`) и `price` — ВЕРДИКТ-НЕСУЩИЕ** (резолвнутая капабилити фолдится в снапшот): любая их правка обнуляет чекпойнты и перекупает прогон ⇒ трогать ТОЛЬКО до первого платного вызова работы C, с явной записью в отчёт; ослаблять `echoMineViolation` или флаг `echoes_when_thinking_off`**нельзя вообще** · **`docs/` — чужая зона: твоё в ней РОВНО два места** — свой отчёт в `docs/archive/reports/` и секция `## Бэкенд` в `docs/PROGRESS.md`; `docs/experiments/*` (зона полигона, включая `00-provider-quirks.md`), `docs/architecture/*`, `docs/product-requirements.md` — только ЧИТАТЬ, находки для них — пунктом отчёта, правит оркестратор · **thinking у DeepSeek НЕ выключать ни в одной конфигурации** (проба C прошлой сессии была карантинным ЗАМЕРОМ по вопросу владельца, а не разрешением; боевой гейт `echoMineViolation` цел и трогать его не надо) · **проект `~/books/gu-zhenren/coldrun-b/reprobe/optNoThink/` — СПЕНТ-проба, НЕ резюмить** (его `book.yaml` указывает на карантинную копию models.yaml с выключенным thinking) · `CheapGateVersion`/labels-фриз/голден-фикстуры двигать ТОЛЬКО под работу B по процедуре §4 · слаги моделей не менять без live-фактчека `/models` · ручка #77 (ре-ген эха перед эскалацией) сама не включается — она за владельцем · чужое в дереве не трогать: `START_PROMT.MD`, `frontend/`, `platform/`, `.gitignore` (⚠ **фронт-сессия сейчас ЖИВАЯ и коммитит** — перед любой git-операцией `git status` и опознание чужого; `reset --hard`/history-rewrite запрещены) · НЕ коммитить · тексты книги в отчёт НЕ цитировать (сырьё живёт durable в `~/books`).
---
## §7. Отчёт и СТОП
`docs/archive/reports/EFFORT_HANDLE_<дата>.md`: эхо-шапка · **работа A** (форма шва + ответы на четыре вопроса §0 + числа пробы §3.4) · **работа B** (дизайн, что решено по второй половине дыры, маскированный дифф голдена) · **работа C** — per-узел наблюдения, **оси голоса со знаменателями**, **итоговая цена холодного старта ЦЕЛИКОМ, включая редактуру** (это и есть закрытие строки 16) двумя путями · дефекты: найдено / починено (с тестами) / отложено (с носителем) · **критик полноты против себя** (обязательная секция) · **адверсариальный проход по своим фиксам** · **заявление = команда**: каждое число сопровождено командой, которой получено — приёмка их пере-ранит · git-список тронутого (только своё).
Батарея в отчёт числами: `go build ./...` · `go vet ./...` · `go vet -tags live ./internal/pipeline/` · `go test -race -count=1 ./...` · майнер-парити `TM_MINER_PARITY=1 … -run Parity` EXACT · labels-фриз `TM_CHECKER_LABELS=1` (k4b 10/1/36/99 · k4_inverse 3/0/1/71 · k2 34/1/6/51 · K6 1/6/0/251 · k4a 0/0/0/348) · голден `TestGoldenDeterminism` · `gofmt -l`.
Краткий итог — `docs/PROGRESS.md`, секция `## Бэкенд`, допиши СВЕРХУ; бэклог-таблицу и CURRENT-STATE НЕ трогать. D-номер лендинга проставит оркестратор. **СТОП — приёмка оркестратора.**