Take the double-payment order into the tree with its premise the right way up: the retry works, and the two causes behind it need different answers

This commit is contained in:
heaven 2026-09-11 13:21:18 +03:00
parent fdc514cd50
commit b983d5e516
3 changed files with 380 additions and 2 deletions

View file

@ -0,0 +1,369 @@
# Бэкенд-пак: ЗА ОДНУ ЕДИНИЦУ ПЛАТИМ ДВАЖДЫ — и по двум РАЗНЫМ причинам, которые движок не различает
> Выдан оркестратором №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` снят командой и приложен.
- [ ] Числа сняты ПОСЛЕ последней правки.
- [ ] Всё живое — в ДЕРЕВЕ, а не в письме.
- [ ] Явное «работа завершена, править не планирую». Без этой строки пак считается идущим.
⚠ Перед отчётом сверь КАЖДЫЙ клейм с результатом инструмента ЭТОЙ сессии · если последний абзац отчёта —
план или обещание, сделай его сейчас · правка теста ради зелени недопустима, правка по заказанной смене
поведения — объявляется.

View file

@ -34,6 +34,7 @@
| Бэкенд · ГЕЙТ ВМЕСТО ПРОЗЫ | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 07.09** — акт **D39.225**; промт в `archive/prompts/` с баннером-исходом |
| **Бэкенд · КОНСИСТЕНТНОСТЬ, КОТОРУЮ МОЖНО ПРЕДЪЯВИТЬ** | [BACKEND_CONSISTENCY_YOU_CAN_SHOW_SESSION_PROMPT.md](BACKEND_CONSISTENCY_YOU_CAN_SHOW_SESSION_PROMPT.md) | ✅ **ВЫДАН 11.09**: приоритет №1 владельца — прибор по ОТГРУЖЕННОМУ тексту (два инварианта: одна форма на термин по книге · где у банка есть форма, отгружена она) · запись выбора редакторской волны · дешёвая половина вопроса пост-проверки · совещательные правила линта сида. ⛔ Заказ стоит на замере: расхождения найдены и перечислены (⚠ **ИСПР. 11.09: их СЕМЬ, а не 18** — 11 из восемнадцати оказались ложной тревогой стеммера, ряд **419**), счётчик «промахи» даёт 0 при знаменателе 2, а пара 84/66 УЖЕ печатается — недостают ИМЕНА (держит одна строка `render.go:491`) и счёт по книге целиком. Вне пака: гейт · ряд 409 · актуатор · решение «закон ли для неподписанного» (вопрос владельца) |
| Бэкенд · СТРУКТУРА ГЛАВ (ДИЗАЙН) | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ 11.09 БЕЗ СХОДИМОСТИ, правилом остановки** — акт **D39.245** (`1d2fab6`, 3 пути, $0); результат — `backend/docs/CHAPTER_STRUCTURE_DESIGN.md` (2062 строки), статус ПРЕДЛОЖЕНИЕ до пака стройки. Глава становится ИДЕНТИЧНОСТЬЮ; предметная часть устояла в четырёх кругах опровержения, механика исполнения — нет, и пак стройки обязан начать с прогона миграционной механики ОПЫТОМ. Промт был — [CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md](CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md), выдан 11.09 (актуализирован пятью врезками + 19 правок по опровергателю: мёртвые адреса, две таксономии осей, три «уже построено», собственная зона записи `backend/docs/` с дизайном-оф-рекорд `D15.2` и прибором shiftmap). ⚠ **ПРОЕКТИРОВАНИЕ, НЕ СТРОЙКА** (`D39.136` п.3): дизайн окна большой пере-нарезки — чем адресуется глава и развязка чанкера (161) · карта осей сдвига · банк-окна на chapter-ID · контент-адресуемый resume `D15.2` · IR/адаптеры/индуктор · правило заголовка и не-CJK путь (303) · гранулярность EPUB (302) и выдача (283). Снимает временный отказ интейка книге из одной главы (`D39.221`). Пак 1 «форматы и честный словарь» ОТРАБОТАН и ПРИНЯТ 06.09 — акт **D39.210**, промт в `archive/prompts/`. ⚠ **ПАК 2 (лексиконы не-CJK, корпус, замер оракулом) — НЕ НАПИСАН и ждёт файлов владельца** |
| **Бэкенд · ЗА ОДНУ ЕДИНИЦУ ПЛАТИМ ДВАЖДЫ** | [BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md](BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md) | ⏳ **НАПИСАН 11.09, редакция 2; выдаётся СВЕЖЕЙ сессии по закрытии пака консистентности актом.** Предмет — двойная оплата единицы: на платном прогоне $0.106472 = 25.4 % цены книги ушло в выброшенные первые попытки. ⛔ **Редакция 1 стояла на ПЕРЕВЁРНУТОЙ посылке** («движок лечит болезнь возбудителем») и заказала бы отключить восстановление, которое работает 4 раза из 4; опровергатель это снял — сессия, исполнившая её буквально, отключила бы восстановление трёх чанков из четырёх, отменила `D2` п.2 и сломала `TestRetryableSubset`. ⭐ Настоящая находка редакции 2: **причин ДВЕ, лекарство одно.** Черновик — потолок мал (контроль: 6 успешных вызовов, все ≤8496 прошли сразу, все >8496 упали и прошли на удвоенном; исключений 0), редактор — размышление съело достаточный потолок (2-я попытка 11717 против упавших 16000, $0.071009 = две трети потери в одном вызове). Плюс `reasoning_tokens` не несла числа ни разу за историю проекта (0 строк при 405 базах и 58 886 строках) и молчащая подстановка цены. Ряды **414** · **415** · **422**, пин flash **413** из пака ВЫНУТ |
| Бэкенд · отозванный | активного НЕТ | пак КАЧЕСТВО написан и ОТОЗВАН до выдачи 05.09 (`archive/prompts/…_WITHDRAWN.md`): предмет дешевле пака — точность чекера уже измерена, пере-снять её стоит одной команды. Следующий пак — СТРУКТУРА ГЛАВ (решение владельца 05.09 «любая книга любого формата»), пишется |
| Бэкенд · прежний | активного НЕТ | пак «денежный стоп» ОТРАБОТАН и ПРИНЯТ С ДОФИКСОМ 05.09 — акт **D39.206** (`81a89e9` + `616a8e4`), промт в `archive/prompts/` с баннером исхода. Следующая работа зоны — строки **291** (флаг вместо клина, `D39.204`) · **294** (форма «сколько добавить», `D39.203`) · **296** (точечная перегенерация по промаху глоссария, после 295) |
| Платформа · ПРАВДА У ДВЕРИ | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 11.09** — акт **D39.246** (`49f74d9`, 15 путей, $0). `PD-455` закрыт, `PD-162` наполовину у обеих денежных дверей, `PD-448` разобран и НЕ построен (четвёртый путь принят направлением, строит следующий пак вместе с минором **0.15.0**). ⛔ Приёмка нашла два дефекта ВНЕ карты автора: выживший мутант на единственной проводке `BooksDir` и тихо-зелёное в самом новом гейте. Живое — `PD-162` (эскроу), `PD-466`, `PD-139`. Промт был — [PLATFORM_TRUTH_AT_THE_DOOR_SESSION_PROMPT.md](PLATFORM_TRUTH_AT_THE_DOOR_SESSION_PROMPT.md), выдан 11.09: `PD-455` (форма обещает то, в чём дверь откажет — точка роста `Blocked.code`, не закрытый enum вердикта) · `PD-448` (двойной клик по «продолжить» — **исследуй и предложи, НЕ строй**: все три законных пути требуют контрактного минора или миграции) · `PD-162` (прогон книги с исчезнувшим каталогом висит с холдом — валидация каталога ПРИ ДОПУСКЕ). ⛔ Свип сирот и потолок аплоадов СНЯТЫ подписанным пропуском: их гейт — открытая регистрация, которой в закрытой бете нет (`PD-175` + `D39.176` п.1) |

View file

@ -63,8 +63,16 @@
| полигон | `POLYGON_INSTRUMENT_REPAIR_SESSION_PROMPT.md` | — | ⛔ **готов, адресата НЕТ** — все семь живых сессий отработали, свежую поднимает владелец |
| фронт | заморожен | — | разморозка по критерию `NEAR_TERM_PLAN.md` |
**В очереди, написано и ждёт слота:** `BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md` (падение, которое
видно) — выдаётся СВЕЖЕЙ бэкенд-сессии сразу, как c9 закроется актом. Опровергатель по нему запущен.
**В очереди, написано и ждёт слота:** `BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md`, **редакция 2**
выдаётся СВЕЖЕЙ бэкенд-сессии, как c9 закроется актом. ⛔ **Опровергатель нашёл 17 находок, и первая
перевернула пак:** я построил §1 на «движок лечит болезнь возбудителем», а замер говорит обратное —
перевыпуск вернул текст 4 из 4, и в трёх случаях успешный ответ физически не влез бы в базовый потолок.
Моя таблица лекарств отключила бы восстановление трёх чанков из четырёх и отменила бы `D2` п.2 с пином
`TestRetryableSubset`. Пере-снял сам — потолок 8496 делит популяцию успешных черновиков БЕЗ ИСКЛЮЧЕНИЙ
(≤8496 прошли сразу ×3, >8496 упали и прошли на удвоенном ×3), а у редактора наоборот. ⇒ причин две,
лекарство одно — вот это и есть пак. Ещё опровергатель снял: три моих контрольных числа не сходились
(`glob` не дошёл до ~170 баз), `D24.3` и `D39.87` мис-атрибутированы, `temperature` у pro 0.4 а не 0.3,
мест вызова два а не три, «пуста ровно `err`» — пусты пять колонок. Все исправлены.
## Журнал выдач очереди №23