# Бэкенд-пак: ЗА ОДНУ ЕДИНИЦУ ПЛАТИМ ДВАЖДЫ — и по двум РАЗНЫМ причинам, которые движок не различает > Выдан оркестратором №23, 11.09. Зона записи — `backend/`. Пак КОДОВЫЙ, платных вызовов НЕ несёт. > ⚠ **Редакция 2.** Первая была построена на перевёрнутой посылке («движок лечит болезнь возбудителем») и > заказывала отключить восстановление, которое на замере работало. Опровергатель это снял; ниже — > исправленное, и след правки виден в §1. ## 1. Какая проблема и что решит твой результат На живом платном прогоне книги 11.09 **движок заплатил дважды за четыре единицы из двадцати семи**: первая попытка вернула HTTP 200 без пригодного текста, выходные токены были выкуплены и выброшены, вторая попытка вернула текст. Цена выброшенного — **$0.106472 = 25.4 % всей цены книги**. Из них три вызова дали РОВНО ноль знаков (**$0.094835 = 22.6 %**), четвёртый — обрезок с текстом ($0.011637). ⛔ **И вот что надо знать ДО того, как начнёшь чинить: лекарство работает.** Перевыпуск с удвоенным потолком вернул текст **4 раза из 4**. Претензия к нему ДЕНЕЖНАЯ (оплачена выброшенная попытка), а не медицинская. **Не выключай его.** Первая редакция этого промта утверждала обратное, и сессия, исполнившая её буквально, отключила бы восстановление трёх чанков из четырёх. **Настоящая находка — в том, что двойных оплат ДВЕ РАЗНЫХ, а движок применяет к ним одно лекарство.** Пере-снято мною по базе прогона (`bk_ROEHZBD46ALFI43E/project.db`, стадии `draft`/`edit`, `tm_hit=0`): | единица | 1-я попытка | 2-я попытка | что это значит | |---|---|---|---| | `draft` ch1u0 | comp **8496** · `length` · пусто · $0.012044 | **15869** · `stop` ✓ | потолка не хватило | | `draft` ch2u1 | comp **8496** · `length` · обрезок · $0.011637 | **13004** · `stop` ✓ | потолка не хватило | | `draft` ch3u0 | comp **8496** · `length` · пусто · $0.011782 | **14034** · `stop` ✓ | потолка не хватило | | `edit` ch2u0 | comp **16000** · `length` · пусто · **$0.071009** | **11717** · `stop` ✓ | потолка ХВАТАЛО | ⭐ **Контроль, и он режет популяцию без единого исключения.** Успешных черновых вызовов в прогоне ШЕСТЬ, их размеры: `6468 · 7932 · 8243 · 13004 · 14034 · 15869`. **Все три, что уложились в 8496, прошли с первого раза. Все три, что не уложились, с первого раза упали и прошли на удвоенном потолке. 6 из 6, исключений ноль.** ⇒ на черновике это НЕ болезнь размышления и не случайность: **первый потолок просто меньше того, что эта стадия реально производит на половине единиц**, и удвоение делает работу, которую должна была сделать оценка бюджета. ⛔ **А четвёртый случай — противоположный, и он стоит ДВУХ ТРЕТЕЙ всей потери в ОДНОМ вызове.** У `edit` потолок 16000 был заведомо достаточен: вторая попытка уложилась в 11717. Значит первый вызов выкупил 16000 токенов чего-то, что не было текстом. Это размышление — у DeepSeek оно считается ВНУТРИ выходных токенов (`reasoning ⊆ completion`), и здесь удвоение потолка действительно даёт ему вдвое больше места. **Что решит твой результат.** Движок научится отличать «не хватило потолка» от «потолок съело размышление» и перестанет платить за первое по цене второго. Плюс причина станет ВИДНА: сегодня спросить «что случилось» нечем — колонка `reasoning_tokens` не несла числа ни разу за историю проекта, а цена считается подстановкой, о которой никто не узнаёт. ⛔ **Чего этот пак НЕ делает:** не выключает перевыпуск (он работает) · не меняет боевые значения `reasoning` в конфигах (ждёт полигонного замера, §4.2) · не правит пин цены flash (данные, ряд 413) · не трогает `books/` · не заводит новых стадий пайплайна. ## 2. Зона записи и git Зона — **`backend/`**. Читать можно всё; писать — только в свою зону. ⛔ **Ты НЕ коммитишь.** Коммитит оркестратор при лендинге. Готовое дерево оставляешь как есть и сообщаешь. Не делай `git add`, `git commit`, `git stash`, `reset --hard`, `checkout` поверх грязного дерева: в дереве параллельно работают другие сессии, и их незакоммиченные правки стираются безвозвратно. Канон — `CLAUDE.md` §«Git-координация мультисессий», он сильнее этого абзаца. Свои итоги, пинги и вопросы пиши в СВОЮ секцию «Бэкенд» журнала `docs/PROGRESS.md`. Это единственное исключение из «`docs/` — зона оркестратора». ## 3. Карта чтения (ЗАКОН, пять позиций; дальше — только по её ссылкам) 1. `CLAUDE.md` — ты его уже получила. 2. **`docs/experiments/00-provider-quirks.md`, пункт 3 ЦЕЛИКОМ** (подпункты 3а · 3б · 3в · 3г · 3д — именно все пять). ⛔ Читать ДО любой правки адаптеров и вызовов; здесь их копий нет, а §3г переписывался ТРИЖДЫ и две прежние редакции были неверны — читай действующую, не мой пересказ. 3. `backend/README.md` — механика движка. 4. `docs/architecture/12-go-style-notes.md` — норматив стиля, только больные места. 5. Ряды бэклога **414 · 415 · 422** и **413** (`docs/BACKLOG.md`) — носители замеров, на которых стоит пак. **Код-факты добывай САМА.** Все `file:line` ниже — отправные точки, а не истина: истина — дерево. Если адрес уехал, это находка, и она идёт в отчёт. **Ратифицированное, что тебе понадобится (вложено сюда, грепать не надо):** * ⭐ **`D2` — основание пака, и читать надо ОБА уточнения, 2 и 3.** Решение: `finish=length` → ретрай с бо́льшим `max_tokens` до капа, потом флаг; пустой оплаченный → skip+flag (не падение прогона). ⛔ **п.2 ратифицировал, что `empty` ПЕРЕАТАКУЕТСЯ:** «`retry-flagged` переатакует **только** `length`/`empty` на той же модели». Это исполнено и ЗАПИНЕНО — `internal/pipeline/disposition.go:211`=`func (r FlagReason) retryable() bool` `func (r FlagReason) retryable() bool { return r == FlagLength || r == FlagEmpty }`, пин `TestRetryableSubset` (`disposition_test.go:90`=`TestRetryableSubset`). ⇒ **всякое изменение ТОГО, ЧТО переатакуется, отменяет `D2` п.2 и двигает этот пин — это пинг мне ДО кода, а не правка.** Не соблазняйся «подогнать» `TestRetryableSubset` как заказанную смену поведения: она НЕ заказана. ⭐ **п.3 — принцип, который ты распространяешь:** «Детектор вырождения ПЕРЕД удвоением max_tokens… бо́льший max_tokens лишь оплачивает больше повтора… при вырождении сразу флаг, без оплаты цикла». Он исполнен ДЛЯ ОДНОЙ причины (петля повтора, `disposition.go` рядом с `distinctRatio`) и не исполнен для второй — «размышление съело потолок», которую тогда не знали. **Твоя работа — распространить наш же образец, а не завести новый.** * **`D24` п.3** — per-model флор `min_max_tokens` РАТИФИЦИРОВАН схемой (capability-слой `models.yaml`, не комментарий); флор у `deepseek-v4-flash`/`pro` там назван **8000**. ⚠ Подъём `pro` до **16000** — НЕ из ноты: это `backend/configs/models.yaml` (блок `deepseek-v4-pro`, `min_max_tokens: 16000`) по замеру мини-прогона 25.07, и цена того замера — 15.5 % счёта прогона в двух выброшенных генерациях. * **`D39.86`/`D39.87`** (02.08) — гипотеза «поднять флор» ФАЛЬСИФИЦИРОВАНА замером. ⛔ **Но узко, и `D39.90` п.9 это прямо сузил, сняв над-клейм:** «число флора влияет на цену ретрая, а не на проходимость» — незамеренная экстраполяция; **при `low` флор не варьировался НИ РАЗУ**. ⇒ «потолок не рычаг» проверено на вендор-дефолтном эффорте; **на `low` не проверено никем — а черновик прогона шёл именно на `low`.** Это прямо касается §4.1: три из четырёх двойных оплат живут в непроверенной области. * **`D39.87` §2 — приёмочный вопрос, который держит тебя от тиражирования дефекта.** Дословно: «появится ЧЕТВЁРТАЯ такая стадия — она унаследует ручку сама или её опять забудут?». Ответ в дереве уже структурный: единственная деривация стадии для внутреннего вызова — `internal/config/internal_call.go`, единственный словарь эффорта — `ValidReasoningEffort` там же, и оба стережёт статический анализатор `internal/archguard/` («an engine-internal call must derive its stage through config.InternalCall.Stage()»), который гоняется обычной батареей. ⇒ **новое измерение решения обязано ехать ЭТИМ швом.** Рукописных `config.Stage{…}` в шиппинг-коде сегодня ноль — проверь сама и не заведи первый. * **`D39.121`** — тесты и гейты не подгонять под зелень. Правка теста, вызванная ЗАКАЗАННОЙ этим паком сменой поведения, — обслуживание, и её надо ОБЪЯВИТЬ в отчёте (что изменилось, какой тест это описывал, куда уехала гарантия). Правка *чтобы прошло* — недопустима. ## 4. Заказ. У каждого пункта размечена свобода ### 4.1. Две причины — два лекарства — ЯДРО ПАКА · «реши сама и аргументируй» **Что уже построено и что ты ЧИТАЕШЬ, а не заводишь.** Классификация отказа есть и сохраняется: `FlagEmpty`/`FlagLength` (`internal/pipeline/disposition.go:59`=`2xx with no usable text`), `degradedTag` (`stagerun.go:921`=`degradedTag surfaces a flag reason`), колонка `request_log.degraded`. ⭐ **И дискриминатор «размышление внутри выходных токенов» ТОЖЕ уже построен и живёт в ДАННЫХ:** в `backend/configs/models.yaml` у провайдера `deepseek` стоит `reasoning: subset`, у `xai` — `additive`, у `gemini` — `additive_total`; поле объявлено в `internal/config/models.go` (`Reasoning string`, комментарий `subset | additive | additive_total`), резолвер — `providerReasoning()` там же, словарь — `internal/llm/provider_openai.go` (`ReasoningSubset`/`ReasoningAdditive`/`ReasoningAdditiveTotal`). ⇒ **ось «subset против additive» ЧИТАЕТСЯ. Второй разметки не заводить.** Найдёшь расхождение — находка, в отчёт. А выбор лекарства этого не спрашивает: `maxTokensForAttempt(base, escalations)` (`internal/pipeline/disposition.go:481`=`func maxTokensForAttempt`) принимает **счётчик удвоений и больше ничего**. **Что построить: различение двух причин и разные ответы на них.** Приор — опровергается аргументом или замером, и я жду именно опровержения, если оно у тебя будет: | причина | улика в данных | сегодня | приор | |---|---|---|---| | **потолок мал** — ответ не влез | 2-я попытка дала БОЛЬШЕ, чем потолок 1-й | удвоить и заплатить дважды | **чинить ОЦЕНКУ первого потолка**, а не перевыпуск: единица, которой заведомо мало, не должна покупать заведомо провальную попытку | | **размышление съело потолок** | 2-я попытка дала МЕНЬШЕ, чем потолок 1-й (`edit`: 11717 < 16000) | удвоить — то есть дать размышлению вдвое больше места | **ступень эффорта ниже при ТОМ ЖЕ потолке** — но см. ⛔ ниже, величина не замерена | | `length` **с текстом** | обрезок доехал | удвоить | **оставить как есть** — это ровно решение `D2` | ⛔ **ЧЕГО В ТАБЛИЦЕ НЕТ И НЕ ДОЛЖНО БЫТЬ: «не перевыпускать».** На замеренных данных это стоило бы трёх восстановленных чанков из четырёх и отменяло бы `D2` п.2. Вопрос — в ЦЕНЕ попытки, а не в её отмене. ⛔ **И ВОТ ГДЕ ВЫРАСТЕТ ВЕЛОСИПЕД, ЕСЛИ НЕ СМОТРЕТЬ.** «Ступень ниже» требует знать, какие ступени есть у ЭТОГО провайдера, — а словарь эффорта в движке **намеренно провайдер-СЛЕПОЙ**, и это объявлено известным пределом: доккомментарий `ValidReasoningEffort` дословно — «Validating against the RESOLVED model's capability would need a new per-model table — a new mechanism, not a use of an existing one — so it stays a named finding, not a silent gap». ⇒ **пер-модельная таблица ступеней — НОВЫЙ механизм, молча его не заводить.** Три законных выхода, выбирай с доводом: (1) шаг вниз по НЕЙТРАЛЬНОМУ словарю движка; (2) не трогать эффорт вовсе и решать денежную задачу через оценку первого потолка — тогда таблица не нужна совсем; (3) таблица — но это отдельное решение и пинг мне ДО кода. ⚠ Разница с осью `subset`/`additive` выше именно в этом: та **построена и читается**, эта **не построена и без письма не заводится**. ⛔ **ДВЕ ОГОВОРКИ О СИЛЕ УЛИК, обе обязательны в твоём отчёте.** **(а) Величина среза размышления НЕ УСТАНОВЛЕНА.** Квирки §3г: параметр `low` до `pro` ДОХОДИТ (доказано поведенческой пробой 30.08), но «ВЕЛИЧИНА среза по-прежнему не установлена… наблюдённые ×2.52 сняты блоками, разнесёнными на 9 дней». То есть «ступень ниже» — законный ПАРАМЕТР, но не доказанное лекарство. **(б) Весь денежный довод стоит на ЧЕТЫРЁХ одиночных розыгрышах.** Квирки §3д: «два одиночных розыгрыша дефолтного пути `deepseek-v4-pro` разошлись в 38 раз… любая смета DeepSeek, снятая с ОДИНОЧНОГО прошлого вызова, может ошибиться на порядок… сметы и сравнения нужно строить на ПОВТОРАХ». ⇒ у моих чисел есть ЗНАК и нет ВЕЛИЧИНЫ. Контроль «6 из 6 без исключений» (§1) знак усиливает, но повторами не является. ⛔ **Никаких велосипедов в остальном.** Не заводи вторую таксономию отказов, не пиши свой классификатор ответа, не добавляй параллельный счётчик попыток. Работа — **дать существующему лекарству прочитать существующий класс**. Если существующая классификация окажется непригодной — это находка, назови её и аргументируй, прежде чем строить рядом. ### 4.2. Ручка усилия у редактора — ⛔ НЕ ТРОГАЙ БОЕВОЙ КОНФИГ · «делай РОВНО так» Соблазн: «на `edit` стоит `reasoning: "off"`, вызов всё равно думает — поставим `low`». **В этом паке не делай.** Причина не та, что подскажет интуиция: * **Ручка есть и ратифицирована.** `ReasoningNone` — OpenAI-совместимая база: `low|medium|high` уходят как `reasoning_effort`; ничего не эмитят только `off` и пустая строка, и тогда едет вендор-дефолт `high`. Именно `off` стоит у редактора в `backend/configs/pipeline-c1.yaml` — **это ВЫБОР конфига, а не невозможность.** Проверь чтением `internal/llm/capability.go` сама. * **«На pro рычага нет» больше не правда.** Вендор сменил маппинг; полигон снял поведенческую пробу 30.08 за $0.038276 и показал, что `low` до `pro` доходит. Живая запись — квирки §3г. * ⇒ **ось «`off` против `low`» измеряется ОТДЕЛЬНЫМ КОНФИГОМ КНИГИ. Это ДАННЫЕ**: ни правки Go, ни снятия защиты; гейт эхо-мины требует `off` только у шиппинг-конфигов `pipeline-c1`/`c2`. * ⛔ **Цена рычага измерена с другой стороны:** `effort: "low"` **пере-вооружает эхо-мину** — на плотном CJK с ослабленным размышлением модель возвращает ИСХОДНИК под HTTP 200. ⚠ Честно про силу этой улики: в квирках это **1 чистый китайский выход из 16**, то есть знак есть, а «воспроизводимо» было бы преувеличением. Единственная чистая проба на `low` шла на АНГЛИЙСКОЙ паре и предупреждения не снимает. ⇒ это **РАЗМЕН «дорого и пусто» против «дёшево и эхо»**, и вслепую его не разменивают. Замер закажет оркестратор полигону, на плотном CJK. **Твоя работа — две вещи, обе без правки боевых значений.** (а) Построй §4.1 так, чтобы ступень усилия была ПАРАМЕТРОМ решения, а не константой, — через шов `internal_call.go`. (б) Напиши в отчёт ОДНИМ абзацем заказ полигону: что меряем, на каком входе, **сколько повторов** (см. §3д), какой результат означает «меняем», какой — «не меняем». Я отдам его дословно, поэтому пиши как заказ, а не как пожелание. ### 4.3. `reasoning_tokens` не несла числа ни разу · «реши сама и аргументируй» Замер оркестратора 11.09: `request_log.reasoning_tokens > 0` — **0 строк**. Контроль рядом: найдено **448** файлов `*.db`, таблицу `request_log` несут **405**, прочитано **58 886** строк, колонка объявлена схемой и присутствует во всех. ⇒ единственная колонка, которой доказывают «потолок съело рассуждение», структурно молчит, и её ноль неотличим от «рассуждения не было». Причина известна и не баг нашего кода: DeepSeek считает рассуждение внутри `completion_tokens` и отдельным полем usage не присылает. Но следствие наше: **прибор врёт молчанием** — а именно этим прибором отличают две причины из §4.1 друг от друга. Реши сама и аргументируй: вынуть число из тела ответа, где оно есть (у DeepSeek приходит `reasoning_content` — проверь), либо честно пометить «провайдер не сообщает» так, чтобы ноль перестал читаться как измеренный. ⛔ Запрет один: **не оставляй ноль без различения.** ### 4.4. Признак отказа и прибор, которым смотрят деньги · «реши сама и аргументируй» ⚠ Формулировка ряда 414 была неверна и исправлена 11.09 — не наследуй старую. Измеренная правда по базе прогона (33 строки): провал виден **тремя** колонками, и все три сходятся ровно на $0.106472 — `ok = 0` на четырёх провальных и `1` на остальных 29 · `finish_reason = 'length'` на тех же четырёх · `degraded` (`empty` ×3, `length` ×1). Из НЕСУЩИХ колонок пуста `err`. ⚠ Пуста не она одна: единственное значение по всем 33 строкам дают также `reasoning_tokens` (предмет §4.3), `cache_creation_tokens`, `estimated`, `est_tokens` — то есть «пуста ровно `err`» было бы отрицательным замером без контроля. ⇒ работа НЕ в том, чтобы «добавить носитель», их три. Работа — решить и обосновать: должен ли `err` заполняться на 2xx-без-текста (против: это не ошибка транспорта; за: рука аудитора тянется к `err` первой), и достаточно ли того, что `ok=0` уже есть. **Возможен полностью законный исход «менять нечего, а надо назвать»** — тогда так и напиши с доводом; это результат, а не увиливание. ### 4.5. Цена считается по модели, которой нет в каталоге · «делай РОВНО так» в части замера, «реши сама» в части лечения Замер оркестратора 11.09 по всем базам машины (контроль: 448 файлов `*.db`, 405 с `request_log`, 58 886 строк): **365 строк оплачены ПОДСТАНОВКОЙ** — провайдер вернул слаг, которого в каталоге нет. ⚠ Из них **344 боевые** (`deepseek-v4-flash` → `deepseek-flash`, 296 строк, $2.049737; `local-qwen3-8b` → `huihui_ai/qwen3-abliterated:8b`, 48 строк, $0) и **21 — тестовые фикстуры** (`m-req`→`m-act`, `fake-model`), которые обязаны быть вычтены: иначе $5.51 фикстуры уедут в денежный вывод. **Сегодня это безвредно:** подстановка падает на ЗАПРОШЕННУЮ модель той же семьи. ⛔ Опасна не подстановка, а её молчание. `PriceForResponse` (`internal/ledger/pricing.go:61`=`func (p *Pricer) PriceForResponse`) пробует `actual`, потом `requested`, потом якорь — и ни одно из **двух** мест вызова (`stagerun.go:781`=`PriceForResponse(model, modelActual)`, `reprice.go:134`=`ledger.CostUSD(r.Pricer.PriceForResponse`) не различает, какая ветвь сработала. Комментарий там же охраняет РОВНО ОДНО направление («премиальный ответ не должен биться по дешёвому дефолту»); обратное — когда провайдер маршрутизирует ВНИЗ — не охраняет никто: запрошен `pro`, ответила модель дешевле, счёт по пину `pro`, $3.96 за 1M выходных против $1.20 вендорских у flash = **×3.3 из кармана читателя, молча**. **Делай РОВНО так:** пере-сними число сама и напечатай рядом контроль — сколько баз прибор открыл и сколько строк прочёл, и **объяви фильтр** (боевые против фикстур). Моё число на слово не принимай: я уже публиковал занижённый контроль, потому что мой первый прибор не дошёл до ~170 баз. **Реши сама и аргументируй**, как сделать подстановку наблюдаемой. Каталог — файл данных (`backend/configs/models.yaml`), и «завести слаг, который провайдер реально возвращает» законно; но слаги не меняются без live-фактчека вендор-доки (гардрейл `CLAUDE.md`), и «слаг живой» ≠ «модель та же». Нужен вендор-заход, которого ты сделать не можешь, — это пинг, а не догадка. ### 4.6. Параметр, который едет по проводу и ничего не делает · «реши сама и аргументируй» Вендор-дока DeepSeek, дословно (снято полигоном): *«Thinking mode does not support the `temperature`, `top_p`, `presence_penalty`, or `frequency_penalty` parameters… setting these parameters will not trigger an error but will also have no effect»*. А ростер шлёт `deepseek-v4-pro` на стадии `edit` **`temperature: 0.4`** (`backend/configs/pipeline-c1.yaml`; на `draft`/flash стоит `0.3`, и оно же уезжает на `pro` при эхо-эскалации), и размышление у `pro` включено всегда. ⇒ **параметр в теле запроса есть, эффекта нет.** Само по себе денег не стоит. Стоит другое: `temperature` **входит в `RequestHash`** (`internal/pipeline/render.go`, поле `req.Temperature`) — движок держит в ключе чекпоинта величину, которая на этом классе моделей ни на что не влияет, и правка «настроим температуру редактора» выглядела бы настройкой качества, а была бы громким `--resnapshot` без единого изменения в ответах. Реши сама: снимать параметр на таких моделях (это смена ключа — назови цену) либо оставить и объявить в схеме модели, чтобы следующая смена не крутила мёртвую ручку. ⛔ Вендор-факт проверь сама по квиркам ПРЕЖДЕ чем решать — я его цитирую, а цитата не улика. ### 4.7. Чего НЕ брать Не выключать перевыпуск (§1) · не менять боевые `reasoning` (§4.2) · не править пин цены flash — ряд 413, это данные и отдельное решение, и рядом висит неразрешённый вопрос, алиас ли `deepseek-flash` · не трогать `books/` · не заводить новых стадий пайплайна. ## 5. Самопроверка ИСПОЛНЕНИЕМ — и где у ЭТОГО пака мягко Мандат: после того как работа сделана, проведи **адверсариальный проход по СВОЕЙ готовой работе**. Глубину и веер выбираешь сама. Субагенты РАЗРЕШЕНЫ явно. Артефакт — таблица «посадка → выжила/поймана → ЧЕМ поймана», в отчёте, с выжившими поимённо. ⚠ **Длинная сессия: раз в несколько часов работы — интервальная самоверификация субагентом против ЯВНЫХ критериев этого промта** (`D39.121`), а не «перечитаю сама». ⛔ **Мутация засчитывается по ТЕКСТУ падения, а не по факту красноты.** Правый вердикт по неправой причине — дыра, а не поимка. ⛔ **Копия под мутацию защищается ПОСТРОЕНИЕМ, а не `set -e`:** перед любой правкой в последовательности — `test -f go.mod` и сверка `pwd` с ожидаемым путём; копия живёт ВНЕ общего скретчпада. **Четыре места, где мягко ИМЕННО У ЭТОГО пака:** 1. ⛔ **Пин на «не перевыпустили» вакуумен по построению.** Утверждение «второго вызова не было» удовлетворяется фикстурой, где второй вызов невозможен ни по какой причине. Нужна фикстура, где он ОБЯЗАН прозвучать при старом поведении и ДОЛЖЕН молчать при новом, и обе половины исполняются. 2. ⛔ **Пин может удовлетворяться ЧУЖОЙ уликой.** Если фикстура гонит попытку через провод, признак «ступень снижена» может прийти не от твоего решения, а из поля предыдущей попытки. Спрашивай не «покраснело ли», а **ЧТО ИМЕННО удовлетворило утверждение**. 3. ⛔ **Денежный пин, флейковый на мутанте, измеряет пустой сценарий.** Гоняй его НЕ ОДИН раз и строй фикстуру так, чтобы деньги были НЕИЗБЕЖНЫ, а не вероятны. 4. ⛔ **Шов между решением и вызовом тоньше самого решения** (урок предыдущего пака этой зоны, 11.09: прибор был верен, провод от прибора к прогону не запинен ничем, и подмена аргумента прошла батарею зелёной). Спроси у своего кода: сколько тестов зовут путь, на котором причина доезжает до выбора лекарства, — и напечатай это число рядом со знаменателем (сколько тестовых файлов прибор прочёл). ## 6. Предметные оси ревью Три, замена любой с аргументом: 1. **Деньги** — каждая новая ветвь отвечает числом, сколько вызовов покупает и сколько экономит. 2. **Детерминизм и снапшот** — что из правки входит в `request_hash` и в `maxTokensPolicyVersion`; двинулось ли что-нибудь, что заставит пере-оплатить книги в переводе. Ответ исполнением, не рассуждением. 3. **Общность движка** — «заработает ли пара, которой в репо ещё НЕТ, без правки Go?». Причина отказа и таблица лекарств не ветвятся по паре или книге. ⚠ Свойство биллинга провайдера уже живёт в данных и **читается** (§4.1) — его заводить не надо. ## 7. Записка-план До первой правки кода положи в свою секцию `docs/PROGRESS.md` записку-план: что берёшь, в каком порядке, что считаешь опасным. Перед сдачей — механическая сверка комплектности против §4, по пунктам, таблицей. ## 8. Заявление = команда Каждое число и каждая категорика в отчёте — **с командой, которой получены**. Приёмка пере-ранит и сверит. Число без команды считается неснятым. ⛔ **Отрицательный замер обязан доказать, что спросил существующее.** Рядом с любым нулём ПЕЧАТАЕТСЯ контрольная величина: «файлов прибор прочёл: N», «баз открыто: N». Не упоминается — печатается. ⚠ **Дифф `^func Test` — ИСПОЛНЕНИЕМ, а не по памяти:** список добавленных и изменённых тестов сними командой и приложи, иначе «я добавила пины» остаётся самоотчётом. ## 9. Эхо-протокол старта **Первым действием, ДО работы**, вернись ко мне эхом ≤10 строк: **скоуп / инварианты / чего не делаю** — ⚠ **СВОИМИ СЛОВАМИ.** Дословный пересказ подтверждает канал, но не понимание, и повторяет ошибку промта вместе с ним. Прямо скажи: **что поняла, что считаешь опасным, что считаешь неверным.** ⚠ Этот промт уже один раз стоял на перевёрнутой посылке и был переписан — возражение в эхо здесь особенно уместно. ## 10. Что НЕ удалось Обязательная секция отчёта. ⛔ И вторая её половина, без которой она вырождена: **«ГДЕ ПРИБОР СЛЕП И Я ЭТО ЗНАЮ»** — что невидимо · почему не чинила · чем закрывается. Вопрос не «что не получилось», а **«что ты знаешь и не сказала»**: в коде названо, а в отчёте нет — значит для следующей смены НЕ названо. ## 11. Канал вопросов и право сказать «не надо» Конфликт промта с кодом или доками → **пинг мне, НЕ интерпретация**. Ты вправе сказать «этого делать не надо» с аргументом — это исполнение пункта, а не отказ. ⛔ Особенно про §4.1: если существующая классификация окажется непригодной, я хочу знать письмом, а не увидеть рядом вторую. ⚠ **Правка промта на ходу доезжает ТОЛЬКО релеем:** я передаю её отдельным сообщением, ты подтверждаешь эхом и называешь ОТДЕЛЬНЫМ пунктом отчёта. Изменение, о котором не было релея, исполнению не подлежит. ## 12. Прямой канал Механизм — `CLAUDE.md` §«Связь между сессиями»; здесь не повторяется. Впиши свой блок в `/tmp/textmachine-channel` первым делом, там же ищи адреса. Эхо (§9) шли по адресу оркестратора из этого файла — оно же проверка канала. ⚠ **Нужной роли в файле нет, файла нет или имя не отвечает ⇒ КАНАЛА НЕТ, и это НОРМАЛЬНЫЙ случай: НЕ опрашивай сессии подряд** — вопрос секцией в отчёт, работа продолжается. ## 13. Критерий завершённости Работу доводишь до конца САМА. Приёмка идёт последней и вторым кругом твоей сессии не служит. - [ ] У каждого пункта §4 — исход: сделано · **не делаю с доводом** · пинг. - [ ] Круги СОШЛИСЬ: последний проход не дал НОВЫХ находок; прежние закрыты таблицей «находка → что сделано → **ЧЕМ ПРЕДЪЯВЛЕНО**». - [ ] Таблица мутаций полная, **выжившие названы поимённо**. - [ ] Дифф `^func Test` снят командой и приложен. - [ ] Числа сняты ПОСЛЕ последней правки. - [ ] Всё живое — в ДЕРЕВЕ, а не в письме. - [ ] Явное «работа завершена, править не планирую». Без этой строки пак считается идущим. ⚠ Перед отчётом сверь КАЖДЫЙ клейм с результатом инструмента ЭТОЙ сессии · если последний абзац отчёта — план или обещание, сделай его сейчас · правка теста ради зелени недопустима, правка по заказанной смене поведения — объявляется.