textmachine/docs/BACKEND_ENGINE_KNOWS_SESSION_PROMPT.md

325 lines
40 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.

# Пак бэкенда: «ДВИЖОК УЖЕ ЗНАЕТ — ДОВЕЗИ ДО ЧИТАТЕЛЯ»
## 1. Какая проблема и что решит твой результат
Движок **вычисляет правду и пишет её себе в лог**, а тот, кто по этой правде принимает решение —
владелец на экране подписи банка и оператор, читающий траты, — смотрит в другое место. Это один класс, а
не три случая, и он сегодня стоит дороже всего остального в очереди.
- **Полнота банка.** `internal/pipeline/terminologist.go:99-103` считает `Consolidated` · `Declined` ·
`Unanswered` · `BatchesDropped` · `ClassifyBatchesDropped`; `:582` предупреждает — «this bank is
PARTIALLY consolidated — a budget cut a pass». В `Log`. А носитель, который читает платформа и с
которого строится экран подписи, — `internal/pipeline/bankexport.go` (`BankExport`: `bank_version`,
`book_id`, `total`, `signed`, `as_of`, `run_id`, `terms`, `proposed`) — полей полноты **не несёт вовсе**.
Наблюдение платформы за живым потоком **04.09** (H10/H11, строка 199): консолидировано 38,
**не спрошено ни разу 47**, срезано бюджетом 5. Владелец подписал бы такой банк как полный.
- **Деньги.** `internal/pipeline/waverun.go:218` складывает в итог волны только `CostUSD` рендер-паса;
`ClassifyCostUSD` (`terminologist.go:145`) в `TOTAL (this run)` не попадает. А разложение «за что
заплачено» относит классификатор в `superseded`, потому что обе банк-роли адресуются ОДНИМ именем стадии
и делят позицию `{глава, чанк, стадия}`. Замер прогона 08.09 по строкам леджера: терминологическая фаза
стоила **$0.013608** — классификатор `$0.009013` плюс два рендер-батча `$0.002775` и `$0.001820`; в итог
волны входит только рендерная половина, а разложение печатает `$0.004595 (2)` — ровно два последних.
Оператор читает о своих деньгах два числа, не сходящиеся с сырым леджером — ровно тогда, когда решает,
продолжать ли платить.
**Что покупает твой результат.** (1) Владелец видит на подписи, что банк неполон, ДО того как подписал.
(2) Числа отчёта сходятся с леджером. (3) Ни один байт снапшота, хеша запроса, ключа манифеста и версии
встроенных данных не двигается — пак **$0** и ничего не перекупает.
## 2. Зона и git
**Зона записи — `backend/`** плюс свой подзаголовок в секции «Бэкенд» `docs/PROGRESS.md`. Чужие зоны
(`platform/`, `frontend/`, `eval/`, `books/`) не трогаешь ни строкой; `docs/` кроме своей секции — тоже.
**Ты НЕ коммитишь**: дерево готовишь и передаёшь оркестратору (канон — `CLAUDE.md`
§«Git-координация мультисессий»). Никаких `git add -A`, `reset --hard`, переписывания истории.
⚠ Заведи подзаголовок `#### Пак «движок уже знает»` внутри секции «Бэкенд» и пиши только под ним: файл
коммичу я, и чужой текст уедет под моим сообщением. `.env` не читать никогда.
## 3. Карта чтения — ЗАКОН, дальше только по её ссылкам
1. `docs/BACKLOG.md` — строки **253** (обе половины) · **357** · **358** · **355** · **312**. Греп `^| 253 |`.
Строка — носитель заказа; расхождение строки с этим промтом — пинг, не интерпретация.
2. `docs/architecture/18-bank-ontology.md`**нормативная страница банковых проекций**: три правила
дисциплины проекции и два класса дефектов. Ты добавляешь поля в проекцию банка ⇒ читаешь ДО работы.
⚠ Там же эррата 07.09 про класс A, заведённый сознательно.
3. `docs/architecture/15-money-path.md` — денежный путь: что чем считается и где леджер есть нижняя граница.
4. `docs/architecture/12-go-style-notes.md` — норматив формы («least mechanism», размер не критерий сплита,
интерфейс объявляет потребитель, код английский).
5. `backend/cmd/tmmutate/main.go`**шапка целиком** (контракт мутационного инструмента) плюс
`backend/Makefile`, цели `battery` и `mutations`.
Ратифицированное, нужное по существу, вложено сюда и похода в журнал решений НЕ требует:
- **`D39.226`** — акт предыдущего пака этой зоны: что уже построено (узкий фильтр банка, пометка `NOT ASKED`
в текстовом сайдкаре, каталог мутаций 259/48).
- **`D39.225` п.10** — ⛔ **правило остановки, применённое к секции предложений сайдкара: четвёртый круг по
ней НЕ открывается** (строка 353 — гейт «появится читатель»).
- **`D39.104` п.1** — банк на проводе есть ЗАКОН для всех ролей независимо от статуса строки. Отсюда цена
неполного банка: владелец подписывает то, что станет законом для всей книги.
- **`D39.183`** — запрет править тесты «чтобы прошло» про МОТИВ; правка от ЗАКАЗАННОЙ смены поведения —
обслуживание, но объявляется в отчёте.
- **`D39.208` п.5** — фикстура, где две различные величины совпали, делает весь их класс невидимым.
- **`D39.216`** — где нынешняя форма не выдерживает заказ, её РАЗВЯЗЫВАЮТ или переписывают, а не обкладывают
заплатой; «дешевле подпереть» само по себе доводом НЕ является. Отступление законно, но объявляется с
ценой: почему заплата дешевле развязки и что именно она откладывает. ⚠ Второй носитель одного знания не
заводится: если правка требует держать две копии в согласии — развязка назрела. Это прямо про §4.1, где
форму полей выбираешь ты.
-**Правило, купленное дважды подряд:** пин на УСЛОВНОЕ сообщение обязан иметь фикстуру, где оно ОБЯЗАНО
прозвучать. Тест, утверждающий только молчание, вакуумен ровно там, где сообщение ложно.
## 4. Состав и разметка свободы
### 4.1 Полнота банка доезжает до того, кто подписывает — **состав РОВНО так, форма полей — реши сам**
Три поверхности подписи, и правда есть только в логе:
- **машинный сайдкар** `BankExport` (`internal/pipeline/bankexport.go`) — полей полноты нет; его читает
платформа, и с него строится экран;
- **текстовый сайдкар** стоп-таблицы (`internal/pipeline/mining.go`) — несёт пометку `NOT ASKED(…)`
(1 хит), и это единственное место, где она есть;
- **экран стопа на stdout** (`cmd/tmctl/render.go`, `renderSignatureStop`) — про пропущенного кандидата не
знает: хитов `NOT ASKED` там **0**, при том что тот же рендерер УМЕЕТ печатать пер-строчные пометки
(контроль — `invented`).
**Заказ:** довези до ОБОИХ читателей то, что движок уже посчитал. Что именно нести — `Consolidated`,
`Declined`, `Unanswered`, `BatchesDropped`, `ClassifyBatchesDropped` — и в какой форме (плоские поля,
вложенный объект, одно суммарное «полон/неполон» плюс детали) **решаешь ты**; аргументируй в отчёте.
**Что не обсуждается:** (а) поля берутся из УЖЕ вычисленного — ПРОИЗВОДНАЯ от посчитанного (например
бит «полон») законна и приветствуется, запрещён ПЕРЕСЧЁТ полноты вторым механизмом;
(б) версию формата сайдкара (`tm-bank-v1`) НЕ бампать — платформа разбирает аллоулистом и новое поле
игнорирует, это ПИНГ ей, а не поломка; (в) на stdout-экране оператор узнаёт о пропущенных кандидатах. ⛔ **Но НЕ обязательно пер-строчной
пометкой, и вот почему — это ловушка вырожденной фикстуры.** `cmd/tmctl/render.go`: `bankStopStdoutCap =
20`, строки сортируются `reviewRank`, а у не-спрошенной строки `Dst` пуст ⇒ ранг **1000** ⇒ она уходит
ПОСЛЕДНЕЙ и при ≥20 строках проваливается под кап. Фикстура на ≤20 строк будет зелёной, а на боевом банке
оператор не увидит ничего. ⇒ **задано СВОЙСТВО** (оператор со стдаута узнаёт, сколько кандидатов не
спрашивали и почему), **форму выбираешь ты** — сводная строка над таблицей или пер-строчная пометка плюс
работа с капом; и фикстура обязана быть ШИРЕ капа;
(г) секцию предложений сайдкара не трогать — по ней применено правило остановки (`D39.225` п.10, строка 353).
**«ОБА ЧИТАТЕЛЯ» = машинный сайдкар и stdout-экран.** Текстовый сайдкар стоп-таблицы уже несёт `NOT
ASKED` и счётчиков полноты от тебя НЕ требует — трогать его в этом пункте не надо.
⛔⛔ **ПОСЧИТАННОЕ ДВИЖКОМ САМО НЕТОЧНО — не довози его буквально, иначе построишь ложную тревогу.**
Три факта, которые смешивать нельзя:
- **`BatchesDropped` (рендер-пас срезан)** — вот это и есть неполный БАНК: термины не консолидированы.
- **`ClassifyBatchesDropped` (классификатор срезан)** — банк ПОЛОН, не уточнены ТИПЫ. Другой факт.
- **`Unanswered`** — по собственному тексту предупреждения «counts them together with terms the role saw
and did not answer», то есть СЛИВАЕТ «не предложено бюджетом» и «предложено, роль не ответила».
⛔ И предупреждение `terminologist.go` кричит «PARTIALLY consolidated» при `BatchesDropped > 0 **||**
ClassifyBatchesDropped > 0`. На прогоне 08.09 было `batches_dropped=0`, `classify_batches_dropped=1`,
`consolidated=29`, `unanswered=0` — то есть рендер-пас ЦЕЛ, а движок всё равно говорит «неполон».
**вывод «полон/неполон» из `dropped > 0` даёт ЛОЖНУЮ ТРЕВОГУ ровно на этом прогоне** — то, что пункт
запрещает абзацем ниже. Полнота БАНКА выводится из рендер-паса; срез классификатора несётся ОТДЕЛЬНО.
⛔⛔ **ГЛАВНАЯ ЛОВУШКА ЭТОГО ПУНКТА, и наивная проекция в неё падает.** `exportBank` зовётся из ПЯТИ мест
(`bookrun.go:180` run-start/seeded · `bookrun.go:258` run-finished · `mining.go:249` auto-continue ·
`mining.go:269` signature-stop · `status.go:1125` redrive/re-seeded), а `r.lastTerminology` заполняется
РОВНО в одном (`mining.go:170`). Плюс `mining.go:66`: майнинг не сконфигурирован ⇒ терминология не
запускается вовсе. ⇒ на четырёх границах из пяти счётчики нулевые, и «`consolidated=0 unanswered=0`»
прочтётся как «неполноты нет» там, где её просто НЕ МЕРИЛИ. Это класс `D39.202` — прибор, отвечающий на
свой вопрос, — и ровно он куплен строкой 312 из §4.3 («`toc_unresolved = 0`, потому что целей НОЛЬ»).
**Заказ:** «не мерено» и «полно» обязаны быть РАЗЛИЧИМЫ на выходе; форма различения твоя. ⚠ Самый простой
путь — граница: поля осмысленны там, где пас ШЁЛ (`signature-stop`, `run-finished`), и на `run-start/seeded`
их быть не должно вовсе (`omitempty`), иначе «неполон» повиснет на проекции следующего прогона.
**Предъявление — не «поле добавлено», а «читатель видит»:** нужны ТРИ фикстуры, и третья — та, которую
легче всего забыть: (1) банк неполон ⇒ артефакт и экран это НЕСУТ; (2) банк полон ⇒ молчат, ложной тревоги
нет; (3) **терминология не работала вовсе** (граница run-start или майнинг не сконфигурирован) ⇒ выход
говорит «не мерено», а НЕ «полно».
**КЛАСС A ЗАВОДИТСЯ СОЗНАТЕЛЬНО, и это надо ОБЪЯВИТЬ.** `18-bank-ontology.md` требует у проекции
НАЗВАННОГО читателя. У новых полей его сегодня нет: `Consolidated`/`Unanswered`/`BatchesDropped` дают
**0 хитов в `platform/internal/` при 183 go-файлах** (контроль: слово `bank` знают 65 из них). Читатель
появится на платформенной стороне и заказан ПИНГОМ. ⇒ назови класс A в отчёте прямо, как это сделал
предыдущий пак зоны (`D39.225` п.4), а не оставляй читателя подразумеваемым.
### 4.2 Два числа о деньгах, не сходящиеся с леджером — **делай РОВНО так**
- **(а) Итог волны занижен.** `internal/pipeline/waverun.go:218``res.TotalUSD += r.lastTerminology.CostUSD`,
а `CostUSD` по собственному докстрингу — «what THIS run's RENDER phase paid»; классификаторская стоимость
лежит отдельным полем `ClassifyCostUSD` (`terminologist.go:145`, заполняется на `:406`) и в
`TOTAL (this run)` не попадает вовсе. **Замер прогона 08.09 по строкам леджера:** терминологическая фаза
стоила `$0.013608` = классификатор `$0.009013` + рендер-батчи `$0.002775` и `$0.001820`; в итог входит
только рендерная сумма `$0.004595`, то есть **две трети денег фазы до итога не доезжают**.
**ВКЛЮЧИТЬ.** ⚠ Я рассматривала второй вариант («объявить в строке, что классификатор считается
отдельно») и снимаю его: он оставляет `TOTAL` МЕНЬШЕ леджера, а этого пак запрещает ниже и требует §13.
Вариант один. ⚠ Числа выше — из отчёта зоны о том прогоне (`docs/PROGRESS.md`, секция «Бэкенд»), СЫРОГО
леджера того прогона в дереве НЕТ (замер: `.db` новее 07.09 — 0 при 163 в дереве) ⇒ предъявляй на СВОЕЙ
фикстуре, а эти числа бери как описание симптома, не как мишень для воспроизведения.
- **(б) Разложение относит классификатор в «купил ничего».** Обе банк-роли адресуются одним синтетическим
именем стадии (`terminologist.go:65` `terminologyStageName`, проставляется обоим), а разложение ключуется
`pos{Chapter, ChunkIdx, Stage}` без роли (`internal/pipeline/paidtail.go`), и более ранний из двух батчей
с одинаковой позицией уезжает в `SupersededUSD`. Улика живого прогона — три строки леджера
(`0 0 terminology classifier` · `0 0 terminology terminologist` · `0 1 terminology terminologist`) при
печатном «the book's TERMINOLOGY … (2)», где два — последние.
⇒ развести позиции банк-ролей в ключе разложения. Роли различимы уже сегодня (`roleTerminologist` /
`roleClassifier`), и колонка `role` в БД ЕСТЬ (`store/migrate.go`) — **миграция не нужна**.
⛔⛔ **ПРАВИТСЯ ЧИТАТЕЛЬ, НЕ ПИСАТЕЛЬ, и это денежная граница, а не вкус.** Соблазн дать классификатору
СВОЁ имя стадии есть, и он покупает перепокупку: чекпойнты адресуются `EnsureJob(bookID, 0,
terminologyStageName, snapID)`, новое имя стадии = новый джоб = старые чекпойнты не матчатся = **на
резюме классификатор оплачивается заново**. ⇒ имя стадии НЕ переименовывать; роль доезжает до разложения
отдельным полем.
**Но на ВХОДЕ разложения роли нет:** `store.CheckpointUsage` поля `Role` не несёт, и запрос её не
выбирает (`internal/store/ledger.go`, тип и его `SELECT`). ⇒ **правка `internal/store/ledger.go`
РАЗРЕШЕНА и входит в заказ** — это не выход за скоуп.
**И она делает ЛОЖНЫМ комментарий, стоящий в самом предмете:** `paidtail.go` про идентификацию
класса по стадии — «equivalent to reading `checkpoints.role` and needing no store change». Правя
механизм, снимай и это утверждение: держать два носителя одного знания в согласии — ровно то, что
запрещает `D39.216` п.3(б).
**ТЕСТ, КОТОРЫЙ ОБЯЗАН ОСТАТЬСЯ ЗЕЛЁНЫМ И НЕ ПОДЛЕЖИТ ПРАВКЕ:**
`TestAReboughtGlossaryBatchIsStillALoss` (`paidtail_test.go`) держит ДВА вызова в ОДНОЙ позиции и
требует, чтобы ранний уехал в `superseded`. Это ДРУГОЙ случай — перепокупка ОДНОЙ роли при росте
черновикового множества (строка 233), и она остаётся потерей. Твоя правка разводит РАЗНЫЕ роли, а не
амнистирует перепокупку одной. Покраснел он — ты сломала класс, а не починила свой.
⚠ И существующие фикстуры `paidtail_test.go` — ровно вырожденный класс `D39.208` п.5: коллизионного
случая (две РАЗНЫЕ роли в одной позиции) в них нет ни одного, поэтому батарея на этом дефекте зелёная.
Фикстуру, где роли РАЗЛИЧНЫ, заводишь ты.
**Обе половины предъявляются СВЕРКОЙ С ЛЕДЖЕРОМ**, а не чтением кода: тест берёт числа отчёта и числа
леджера на одной фикстуре и утверждает их отношение. ⚠ **Отношение назови точно, это два разных
утверждения:** для (а) — РАВЕНСТВО (итог обязан сойтись с суммой строк), для (б) — что классификаторская
строка попала в свой класс, а не в `superseded`. «Леджер есть нижняя граница» — про мир, а не про этот
тест: там, где движок не записал вызов, равенства нет и быть не может, и это отдельный класс (`PD-441`,
строка 331), в пак НЕ входящий.
**ГРАНИЦА ЭТОГО ПУНКТА, и она снята чтением ОБЕИХ сторон шва — не выводи её заново и не расширяй.**
Это правка ОТЧЁТА, а не денег: платформа биллит НЕ `TotalUSD`, а `committed_usd` — сумму по таблице
`spend` (`platform/internal/runs/reconcile.go` докстринг `attemptSpend`), а классификатор ходит тем же
учтённым путём, что и все стадии (`runBankAttempt``runAttempt`, `terminologist.go:783-796`), и его
деньги в `spend` УЖЕ лежат. ⇒ ни один счёт пользователя не двигается, согласия владельца пункт не требует.
Если тебе кажется, что нужно тронуть ЛЕДЖЕР, — это стоп и пинг: там будет двойной счёт, а не починка.
**СОСЕДНЯЯ МИНА — не в этом пункте, но в шаге от него, и я называю её ГРАНИЦЕЙ, а не заказом.** Твоя
правка `waverun.go` порога согласия НЕ касается: база порога — `projectBookUSD` в
`internal/pipeline/rebill.go`, другая сумма. ⛔ Но если работа поведёт тебя в ПРОЕКЦИЮ (`ProjectedBookUSD`,
`status`, добавка контура к `bookUSD`) — там порог СОГЛАСИЯ на пере-оплату считается БЕЗ банкового контура
НАМЕРЕННО («THE CONSENT THRESHOLD IS COMPUTED WITHOUT THE CONTOUR, and the two numbers part company HERE»),
и **первая версия той правки сложила контур ДО взятия порога и тихо ОСЛАБИЛА денежный гейт; поймала
приёмка.** ⇒ пошла в проекцию — это стоп и пинг, а не «заодно».
### 4.3 Третий член того же класса — **реши сам, бери если по пути**
Строка **312**, остаток Д-3: движок предупреждает о нечитаемом оглавлении в свой `Log.Warn`, а платформа
читает `events.jsonl` — то есть та же форма «знает один, смотрит другой». Проверь сам, тот ли это класс;
если да и если дёшево — закрой тем же движением. Если дорого или предмет другой — скажи в отчёте, оставлю
строкой.
### 4.4 Чего в паке НЕТ — **не делай**, и это ОБЪЯВЛЕННЫЕ сужения
- **Секция предложений сайдкара банка** — по ней применено правило остановки (`D39.225` п.10), гейт
возврата назван строкой 353: появление первого ЧИТАТЕЛЯ секции. Четвёртый круг не открывается.
- **Платформенные половины** строк 253 и 224 — чужая зона. Твоё действие — ПИНГ в отчёте, не правка.
- **Всё, что двигает** снапшот, хеш запроса, ключ манифеста или версию встроенных данных. В этом паке
заказанных смен поведения ДВЕ, и обе — в путях отчётности и артефактов: §4.1 и §4.2.
- ⛔ **СТРОКА 360 — ТАЙМАУТ ЧТЕНИЯ, МАСКИРУЮЩИЙСЯ ПОД НЕЧИТАЕМОЕ ТЕЛО. НЕ БЕРЁШЬ, и я называю это прямо,
потому что она ДОРОЖЕ всего, что в этом паке.** Замер полигона: 23.834.5 % цены редакторской руки;
ошибка чтения выбрасывается в `_`, обрезок биллится по оценке и РЕТРАИТСЯ. Почему не здесь: (1) это
другой класс — не «движок знает, а читатель не видит», а «движок не знает, что оборвал сам»; (2) её
денежная половина требует слова владельца (платит ли пользователь за вызов, который оборвали МЫ) и
сцеплена со строкой **331**, где тот же предмет закрыт ПРОТИВОПОЛОЖНО — нулём вместо оценки. Идёт
отдельным паком. ⚠ Наткнёшься на неё по пути — **не чини, отдай наблюдением в отчёт**: молчаливая
починка здесь смешает два денежных класса в одном лендинге.
- **Строки 261 · 262 · 331** — члены того же класса «вызов оборвали мы»; уезжают вместе с 360.
- **Переписывание `Runner`, дробление `internal/pipeline`** — запрещены `D39.224` §7. ⚠ «Новый детектор
границ» там же — не запрет, а УСЛОВИЕ; к твоим задачам не относится, но силу пункта не путай.
- **Платные вызовы** — ноль, см. §Деньги.
## 5. Мандат самопроверки ИСПОЛНЕНИЕМ — «перечитал сам» не считается
1. **Каждая новая гарантия предъявляется МУТАЦИЕЙ**, и краснота читается ПО ТЕКСТУ падения: говорит ли он
про сломанное тобой. Правый вердикт по неправой причине — дыра, а не поимка.
2. **Мутации — ТОЛЬКО на копии дерева, и рецепт копии УЖЕ СТОИТ в цели `mutations` (`backend/Makefile`)**
своего однострочника не пиши, он станет вторым носителем и разойдётся. Гонишь батарейное подмножество —
`make mutations`. Гонишь одиночную посадку `-id`, то есть копию делаешь руками, — повтори цель ДОСЛОВНО:
распаковка в **подкаталог** `$tmp/backend` (а не в корень времянки), исключения `--exclude='./.env*'` и
`--exclude=./bin`, и **два** трапа: `EXIT` убирает, а `INT TERM` убирает И ВЫХОДИТ (`exit 130`) — иначе
прерванный прогон продолжает жить. Рабочее дерево не правится никогда, в том числе «на секунду».
3. **Мутируй и СВОИ новые фикстуры.** ⛔ И особая осторожность здесь: у тебя ДВА условных сообщения (неполный
банк · пропущенный кандидат). Пин на каждое обязан иметь фикстуру, где оно ОБЯЗАНО прозвучать, и вторую,
где оно обязано молчать. Предыдущий пак этой зоны выпустил ТРИ ложных операторских предупреждения подряд,
и все три были зелёными именно потому, что утверждалось только молчание.
4. **Новые записи — в каталог** `cmd/tmmutate/mutations.json`. Ключи СТРОЧНЫЕ: `id`, `package`, `why`,
`edits`, плюс необязательный `battery`. ⚠ `package` — пакет ПИНА, а не пакет правки: инструмент гоняет
один пакет. ⚠ `battery` ставится не всем: сегодня это **48 записей из 259** — гейтовое подмножество;
какие из твоих туда входят, решаешь ты и говоришь почему.
5. **BASELINE НА ВХОДЕ** (снят оркестратором 08.09): `make battery` → 19 `ok` · 0 `FAIL` · 4 «no test files» ·
4 скипа поимённо; `make mutations`**48 записей · 0 unexpected · `anchors swept: 0 of 259`**. Красное на
входе — находка и пинг, а не твоя работа.
6. **Субагенты РАЗРЕШЕНЫ ЯВНО** — и на охват, и на адверсариальный проход по ГОТОВОЙ работе. Направление,
что здесь уязвимее всего: (1) вторая половина утверждений про условные сообщения; (2) сверка отчёта с
леджером — легко заявить и трудно доказать; (3) фикстуры, где «полон» и «неполон» случайно совпали.
7. **Проверяемый артефакт** — таблица мутаций в отчёте: `мутация → где посажена → красный тест → ТЕКСТ
падения`, и отдельной строкой **выжившие**, если такие есть.
## 6. Оси ревью — характер «Код», плюс деньги (ось вправе заменить с аргументом)
Тест утверждает не то, что думает (`D39.171`): часы как способ синхронизации там, где предмет не время, и
ассерт по подстроке в буфере, куда пишет кто-то ещё. Обе формы дают и ложное красное, и **тихо-зелёное** —
оно дороже: зелёное, не проверившее ничего, не перечитывает никто. Третья ось твоя: **деньги** — любое число
выводится из СЫРОГО леджера, и леджер есть нижняя граница, а не оценка.
## 7. Записка-план ДО работы
До первой правки — короткая записка в отчёт: что берёшь, в каком порядке, что считаешь рискованным. После
работы — сверка комплектности против §4 МЕХАНИЧЕСКИ (таблицей), не перечитыванием.
## 8. Заявление = команда
Каждое число и категорика отчёта — с командой, которой получены. Отрицательный замер печатается ВМЕСТЕ с
контрольной величиной, доказывающей, что вопрос задан существующему предмету. Сверх этого, про код:
- **перед отчётом сверь каждый клейм с результатом инструмента ЭТОЙ сессии**, а не с памятью и не с этим
промтом: адреса §4 сняты 08.09 и могли уехать;
- **дифф `^func Test` — исполнением**, ИМЕНАМИ через `comm`, а не разностью счётчиков;
- **последний абзац отчёта оказался планом или обещанием? сделай это СЕЙЧАС**;
- ⛔ править или удалять тест/голден/гейт, чтобы он прошёл, — НЕДОПУСТИМО; исключение — правка от заказанной
смены поведения (§4.1, §4.2), и она объявляется отдельным пунктом;
- сессия длинная — поставь себе интервальную самоверификацию субагентом против ЯВНЫХ критериев §13.
## 9. Эхо-протокол старта
ДО работы — ≤10 строк **СВОИМИ словами**: скоуп · инварианты, которые не имеешь права сломать · что НЕ
делаешь. Дословный пересказ промта подтверждает канал, но не понимание, и ошибку промта повторяет вместе с
ним. Отправь эхо оркестратору по адресу из прямого канала (§12); канала нет — эхо идёт первой секцией
отчёта, и работа продолжается.
## 10. Что НЕ удалось — обязательная секция отчёта
Что не вышло, что не проверено, где сомневаешься. Пустая секция при непустой работе — сама по себе находка;
«не проверено» и «опровергнуто» — РАЗНЫЕ исходы.
## 11. Канал вопросов и право сказать «этого делать не надо»
Конфликт промта с кодом или доками — **пинг, а не интерпретация**: промт мог устареть, и в этом проекте это
случается — за прошлые сутки в промтах и трекере нашли шесть ложных утверждений, три из них авторства
оркестратора. Считаешь пункт ненужным — скажи с доводом; такие возражения ценнее послушного исполнения.
## 12. Прямой канал
Механизм — `CLAUDE.md` §«Связь между сессиями». Впиши СВОЙ блок в `/tmp/textmachine-channel` первым делом,
чужие блоки не трогай. ⚠ Адрес из файла не доказывает, что сессия жива: сверься с `ListAgents`. Нужной роли
нет или имя не отвечает ⇒ **канала нет, и это НОРМА: не опрашивай сессии подряд** — вопрос секцией в отчёт.
## 13. Критерий завершённости — по нему тебя примут
Работу доводишь до конца САМ; приёмка идёт последней и вторым кругом твоей самопроверки не служит.
Готово — это когда:
- у КАЖДОГО пункта §4 есть исход: сделано · не делаю с доводом · пинг;
- круги СОШЛИСЬ: последний проход не дал НОВЫХ находок, прежние закрыты таблицей
«находка → что сделано → ЧЕМ ПРЕДЪЯВЛЕНО»;
- **каждый класс закрыт с ЗНАМЕНАТЕЛЕМ: «закрыт в N носителях из M»**, и M посчитан командой, а не на глаз;
- таблица мутаций полная, выжившие названы;
- **числа отчёта сверены с СЫРЫМ леджером**, а не с логом;
- числа сняты ПОСЛЕ последней правки, а не вспомнены;
- всё живое — в ДЕРЕВЕ, а не в письме;
- сказано явно: **«работа завершена, править не планирую»**. Без этой фразы пак считается идущим.
## Деньги
**Пак $0.** Платных вызовов ноль: всё, что нужно предъявить, предъявляется на ФЕЙК-провайдере и на
собственных фикстурах. ⚠ **Сырого леджера прогона 08.09 в дереве НЕТ** (замер: файлов `.db` новее 07.09 —
**0** при 163 в дереве, свежайший от 02.09) — числа этого прогона живут только описанием в отчёте зоны, и
воспроизводить их не надо. Если тебе кажется, что задача требует платного прогона, — это стоп и пинг, а не
вызов.
## Отчёт
Секция **«Бэкенд»** в `docs/PROGRESS.md`, подзаголовок `#### Пак «движок уже знает»`: исход по каждому
пункту §4 · таблица мутаций · таблица «находка → что сделано → чем предъявлено» · столбец «закрыт в N из M» ·
сверка отчёта с леджером числами · пинги в чужие зоны (платформенные половины 253 и 224) · §10 «что не
удалось» · вопросы оркестратору.