textmachine/docs/BACKEND_CONSENT_SESSION_PROMPT.md

326 lines
36 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.

# Промт сессии: БЭКЕНД — «число согласия на пути второй покупки»
> **Роль:** бэкенд. **Зона записи:** `backend/`. Чужие зоны (`platform/`, `frontend/`, `eval/`)
> читаешь, не правишь; в `docs/` пишешь ТОЛЬКО в секцию «Бэкенд» журнала `docs/PROGRESS.md` — это
> единственное разрешённое каноном исключение. **Не коммитишь** и не трогаешь индекс и историю —
> полный список запретов и причина: `CLAUDE.md`, «Git-координация мультисессий».
> Норматив стиля — `docs/architecture/12-go-style-notes.md`.
## Какая проблема и что изменит твой результат
Продукт продаёт главы **частями**. Покупатель берёт кусок, потом ещё кусок — и на этом пути число,
которым движок спрашивает согласия на оплату, **врёт** (занижает при смене модели стадии и не
раскрывает, какая его часть — старые деньги), а один юнит может быть оплачен дважды. После пака
покупатель, берущий книгу частями, видит число, которое не занижает молча, признаётся, какая его
часть — старые деньги, и не платит второй раз за юнит, который уже двигал; а три гарантии, которые
сегодня держатся только на честном слове, держатся тестом и записью в мутационном каталоге.
## Карта чтения — пять позиций, дальше только по их ссылкам
1. Шапка-таблица `docs/architecture/09-target-architecture.md` — что построено.
2. `backend/README.md` · `docs/architecture/03-implementation-notes.md` (через баннер).
3. `docs/architecture/15-money-path.md` — денежный путь целиком, включая §2 «Деньги ДВИЖКА».
4. Строки **232** и **197** единого бэклога (`docs/PROGRESS.md`).
**Строка 197 — УКАЗАТЕЛЬ, а не фикс-лист:** её собственный текст отсылает тело списка в
`docs/archive/PROGRESS-2026-08-17.md`, запись приёмки №18 (проверь: `grep -n '^| 197 ' docs/PROGRESS.md`).
Определения ФЧ-1 и ФЧ-4, которые ты исполняешь, продублированы ниже В ТЕЛЕ ЭТОГО ПРОМТА — читать
архив ради них НЕ надо. Если всё же захочешь дословный оригинал, законный вход ровно один и он
назван здесь: `sed -n '68p' docs/archive/PROGRESS-2026-08-17.md` (`grep -n 'ФЧ-1 (HIGH' docs/archive/PROGRESS-2026-08-17.md``68`).
Инструкции из архива НЕ исполняются — он источник ФОРМУЛИРОВКИ, приказ несёт этот промт.
5. Журнал решений, два номера грепом: **D39.150** (`grep -n '^## D39.150' docs/architecture/05-decisions-log.md``535`)
и **D39.181** (`grep -n '^## D39.181' docs/architecture/05-decisions-log.md``1712`).
## Состав — четыре пункта, в этом порядке
### 1. ФЧ-5 — ось МОДЕЛИ в числе согласия. Ратифицирован 17.08 (D39.150 п.1) и НЕ построен
**«Делай РОВНО так» — состав ратификации. D39.150 п.1 несёт ЧЕТЫРЕ обязательства, не три:**
1. **прайсить по модели, которую стадия резолвит СЕЙЧАС**, а не по той, что ответила тогда;
2. **при невозможности знать — округлять ВВЕРХ**;
3. **называть оговорку в тексте согласия**;
4. **обещание ТОЧНОСТИ снимается с провода.** Дословно из ноты: «Следствие: обещание точности
снимается с провода — комментарий „`status` не может назвать сумму, отличную от `translate`“ …
при исполнении ФЧ-5 **обязан быть заменён** честной формулой „оценка, ошибается вверх, вот чем“».
Контракт механизма после пака — **не точность, а НАПРАВЛЕНИЕ ошибки: «никогда не занизить молча»**;
завышение безопасно (оператор согласился на большее, чем заплатит), занижение — тот вред, ради
которого чинилась строка 181.
⚠ **Пункт 4 — это правка ТЕКСТА в двух живых местах, и оба надо снять, иначе ФЧ-5 придётся
переоткрывать** (класс «текст обещает больше числа» — ровно тот, ради которого заведён фикс-лист):
- `backend/internal/pipeline/status.go:217`=`can never quote a different number` — «…so status can never quote a different number than the
one translate enforces» (`grep -n 'can never quote a different number' backend/internal/pipeline/status.go``217`);
- `backend/internal/pipeline/reprice.go:24`=`NOT projected (like the content axis` — шапка файла: «NOT projected (like the content axis in
rebill.go): **ROUTING**. A row is priced by the model that ANSWERED, not by the one its stage resolves
to now» (`grep -n 'NOT projected (like the content axis' backend/internal/pipeline/reprice.go``24`).
Это заявление становится ЛОЖНЫМ ровно в тот момент, когда ты исполнишь пункт 1.
Нота помечает ФЧ-5 обязательным **до первого пере-прогона со сменой модели стадии** (D39.150 п.2).
Замер вреда из приёмки: **`$0.003640` против честных `$0.036400`** при `HistoricalRows=0`
(`grep -on '\$0.003640 против честных \$0.036400' docs/archive/PROGRESS-2026-08-17.md``68:...`).
**Проверь исходное состояние САМ и не бери с моих слов.** Противоречащая ратификации шапка — в
`reprice.go` (см. выше); шапка `rebill.go` про это НЕ говорит, там речь про `$0`-строку. Формулируй
по коду, который увидишь.
**«Решаешь сам и аргументируешь»:** форма хранения и проводки «модели, которую стадия резолвит сейчас»
в числе согласия; конкретная формула округления вверх при незнании; точные слова оговорки и обеих
заменяемых формул.
### 2. Строка 232, ВТОРАЯ половина — слот объёмного гранта
Первая половина (читающий метод `store.AnnouncedOnceKeys()`) исполнена 31.08. Живой остаток по строке:
**юнит, прерванный МЕЖДУ волнами (signature stop, денежный потолок, Ctrl-C), тратит слот объёмного
гранта повторно** — покупатель платит за N глав и получает меньше. Замер строки — **4 купленных юнита
→ 2 главы** (`grep -on '4 купленных юнита → 2 главы' docs/PROGRESS.md``175:...`). Разбор —
`backend/docs/MONEY_HONESTY_REPORT.md` §4.5 (`grep -n '^### 4.5' backend/docs/MONEY_HONESTY_REPORT.md``206`).
**Остатков в коде названо ДВА, а не один — и это НЕ одно и то же.** Докстринг, приземлённый тем же
коммитом, перечисляет их под заголовком «⚠ TWO RESIDUALS, named rather than implied»
(`grep -n 'TWO RESIDUALS' backend/internal/pipeline/volume.go``523`):
- **(1) конвейер, получающий ПЕРВУЮ редакторскую стадию**, двигает отгружающую волну с draft на edit,
и уже прочитанные юниты репортятся свежими: «closing it needs a record of what the shipping wave WAS,
which nothing stores today». **ВНЕ СКОУПА этого пака** — закрытие требует носителя того, чем была
отгружающая волна, а пак схему хранилища не трогает (см. ПОРЯДОК ДЕПЛОЯ). ⚠ Строка 232 считает свою
первую половину ИСПОЛНЕННОЙ, а §4.5 отчёта называет только слот — то есть остаток (1) живёт СЕГОДНЯ
только в этом докстринге. **Назови его в отчёте, чтобы он не умер молча вместе с паком.**
- **(2) слот гранта** — В СКОУПЕ, это и есть твой пункт 2.
**Закрытие 232 объявляется ТОЛЬКО по остатку (2).** Написать «строка 232 закрыта» без этой оговорки —
ошибка отчёта.
**«Делай РОВНО так» — ратифицированный ответ на развилку, которую строка 232 требует назвать в
промте.** Ключ анонса несёт ВОЛНУ (`grep -n 'unitOnceKey is the identity' backend/internal/pipeline/events.go`
`396`), значит «юнит отгружен» — факт per-wave, и надо решить, ПО КАКОЙ волне судить. Ответ есть и
он не новый механизм: **отгрузкой считается волна, владеющая ОТГРУЖАЮЩЕЙ (последней) стадией —
`finalStageWave`** (`grep -n 'finalStageWave is the wave that owns the SHIPPING' backend/internal/pipeline/snapshot.go`
`243`; сама функция — `247`). На редакторском конвейере это `edit`, на черновом-только — `draft`.
**Не draft-волна и не «любая»:** иначе черновой-только конвейер получит ось, которая НИКОГДА ничего не
считает отгруженным. Отступление от этого — пинг, не решение.
**«Решаешь сам и аргументируешь»:** где именно вычитается уже потраченный слот, как это переживает
рестарт, и что происходит с частично двинутым юнитом.
**Оговорка веса, проверь её:** платформа сегодня продаёт денежным потолком (`--ceiling-usd`), а
`--max-units` не передаёт вовсе — то есть вред пока не доезжает до пользователя ЧЕРЕЗ платформу. Это
меняет приоритет, но не отменяет дефект. Проводка `--max-units` в платформу — отдельный ГЕЙЧЕНЫЙ пункт
очереди, не твой (`grep -n 'Проводка .--max-units. в платформу остаётся ГЕЙЧЕНОЙ' docs/PROGRESS.md``19`).
### 3. ФЧ-4 — раскрытие «часть суммы — старые деньги»
**У ФЧ-4 ДВЕ половины. Закрыт = ОБЕ.** Дословно из фикс-листа: «раскрытие „часть суммы — старые
деньги“ не доезжает до `status`/`--json` (`HistoricalRows` теряется на границе `status.go`), **а также**
второй операторский текст отказа (`--accept-rebill`) цитирует пере-прайсенное число без basis».
- **(а)** `HistoricalRows` теряется на границе отчёта: `rebillOutcome` возвращает
`(basis, units, usd, outputUnits)` и роняет его (`grep -n 'rebillOutcome' backend/internal/pipeline/status.go`),
в `StatusReport` соответствующего json-тега нет. Покупатель не может отличить новую трату от уже
оплаченной.
- **(б)** текст отказа по названному потолку `--accept-rebill=<usd>` печатает `proj.USD` **без**
`projectionBasis` и **без** `HistoricalRows`, тогда как оба других текста согласия их несут
(`grep -n 'caps consent at' backend/internal/pipeline/rebill.go``425`, ветка
`r.AcceptRebill.Capped`; для сравнения `grep -n 'func projectionBasis' backend/internal/pipeline/rebill.go``305`).
**Инвариант, которому подчиняется этот пункт — закон раскрытия D39.181 п.1** (ратифицирован словом
владельца 31.08, самая свежая норма зоны): четыре обязанности — **не молчать · не называть причину,
которую не сравнивал · ФИГУРА ЕДЕТ С БАЗИСОМ · КАНАЛ НАЗНАЧАЕТ ПОТРЕБИТЕЛЬ**. Ты добавляешь денежную
фигуру на опубликованную поверхность — обе выделенные обязанности про твой случай прямо.
**И не рассчитывай, что сборка тебя поправит:** спроектированный гейт Г1 (реестр раскрытия +
рефлективный конформанс-тест), который поймал бы новое поле без записи, **НЕ построен**
`grep -rn 'Consumers\b' backend/internal --include=*.go | wc -l``0`. Компилятор молчит.
**«Делай РОВНО так»:** обе половины, и фигура едет с базисом. **«Решаешь сам и аргументируешь»:** форма
раскрытия исторических строк на проводе и в операторском тексте (имя поля, форма basis, что печатать
при `HistoricalRows=0`).
### 4. ФЧ-1 — посадки на гарантии, чьи мутации ПЕРЕЖИВАЮТ зелёную батарею
**«Делай РОВНО так» — ограничь ТРЕМЯ, и вот они поимённо.** D39.149 п.5
(`grep -n '^## D39.149' docs/architecture/05-decisions-log.md``523`; сам пункт — `533`) говорит «Три HIGH фикс-листа —
не дефекты поведения, а НЕПРИБИТЫЕ гарантии: мутации переживали зелёную батарею», но поимённо называет
ровно ОДНУ; остальные две живут только в теле фикс-листа. Поэтому все три вписаны сюда, и брать надо
ЭТИ, а не изобретать свои:
1. **Снятие фильтра по книге в `CheckpointUsageForBook`**`WHERE j.book_id = ?` не валит ни store-,
ни денежные pipeline-тесты (`grep -n 'func (s \*Store) CheckpointUsageForBook' backend/internal/store/ledger.go`
`367`, сам фильтр — `371`). Единственная защита от того, чтобы деньги ЧУЖОЙ книги вошли в число
согласия. **Воспроизведено оркестратором ЛИЧНО.** Существующий тест
(`internal/store/ledger_test.go:10`=`func TestCheckpointUsageForBookReturnsInsertionOrder`) пинит только
порядок вставки.
2. **Ключ ячейки можно лишить `chunk_idx`** — обход начнёт съедать деньги соседнего чанка (во всех
фикстурах глава = один чанк, поэтому никто не краснеет).
3. **Ветка пометки «строка не пере-оценена» не ловится на продовом пути**`fromHistory` из
`(rp *repricer) usd` (`grep -n 'func (rp \*repricer) usd' backend/internal/pipeline/reprice.go``113`).
**Честность про источник:** гарантия (1) воспроизведена приёмкой лично, а **(2) и (3) приняты СО СЛОВ
мутационной линзы** — приёмка №18 записала это в свою секцию «что НЕ проверено». Значит первый твой шаг
по каждой из них — **воспроизвести мутацию и увидеть ЗЕЛЁНУЮ батарею**; если мутация на самом деле
краснеет, гарантия уже прибита, и это **находка**, а не повод молча заменить её своей.
**Механизм посадок в зоне УЖЕ ПОСТРОЕН — не пиши четвёртый одноразовый харнесс.**
`backend/cmd/tmmutate` + каталог `backend/cmd/tmmutate/mutations.json` (**73** записи:
`python3 -c "import json;print(len(json.load(open('backend/cmd/tmmutate/mutations.json'))))"``73`).
Его собственный докстринг объясняет, зачем он в репозитории: «Two packs and one acceptance in a row have
each written this harness from scratch in a scratch directory, and it died with the session every time».
Денежного пути в каталоге сегодня НЕТ вовсе — `grep -c 'reprice.go\|rebill.go\|volume.go\|ledger.go' backend/cmd/tmmutate/mutations.json`
`0`, то есть твои три гарантии туда и просятся.
⚠⚠ **ЛОВУШКА РЕПОЗИТОРИЯ, читай прежде чем запускать:** `-root` **без дефолта**, и копия дерева — **не
предложение**. Работать ТОЛЬКО на копии:
```
cp -a backend /tmp/mut/backend && cd /tmp/mut/backend
go run ./cmd/tmmutate -root . # весь каталог
go run ./cmd/tmmutate -root . -id A-lock-arbiter # выборочно
go run ./cmd/tmmutate -root . -logs /tmp/mutlogs # сохранить полный вывод тестов
```
Причина в докстринге прямая: однажды посадка просидела в ОБЩЕМ дереве сорок минут после таймаута, и
независимый ревьюер намерил по нему несуществующий дефект. Здесь параллельные сессии — это цена чужой
работы.
**«Делай РОВНО так»:** ровно три гарантии выше · каждая заводится **ЗАПИСЬЮ в `mutations.json`**, а не
только тестом · `tmmutate` гоняется на копии. **«Решаешь сам и аргументируешь»:** форма самих посадок
(какую строку и как ломать), уровень теста (store / pipeline / оба), формулировка `why` в каталоге.
⚠ Это ПОСЛЕДНИЙ пункт: он берётся, если после первых трёх осталось время. Не превращай в бесконечный
список — если времени хватило только на одну гарантию, это законный исход, назови его в отчёте.
## Границы — что НАМЕРЕННО не берём
- **Строка 160 «структура глав, Этап 0»** — хотя слово владельца 15.08 ставит её ближайшим паком и её
блокер снят 30.08 — строки 231 и 236 в таблице БОЛЬШЕ НЕТ
(`grep -n '236 сужена D39.175' docs/PROGRESS.md``256`). Причина техническая и она в ПОРЯДКЕ ДЕПЛОЯ ниже: строка требует бампа
**формы манифеста**, а параллельно идёт платформенный пак. Берётся отдельно, со стоп-миром.
- **Читающая сторона банка — гейты РАЗНЫЕ, и их надо различать** (проверено по ячейкам:
`for n in 221 224 226 228 230; do grep "^| $n " docs/PROGRESS.md | awk -F'|' '{print $4" | "$5}'; done`):
- **221** — гейт «разморозка фронта», то есть слово владельца. Вопрос вынесен ему отдельно
⚠ (следа этого вопроса в репозитории нет — если тебе это важно для планирования, спроси, а не считай фактом).
- **224** — «держит экран подписи вместе с 221», зона «бэкенд + платформа». **Не берём: ресурс пака.**
- **226** — «диагностики нет вовсе». **Не берём: ресурс пака.**
- **228** — гейт «ЗАКАЗ по читающей стороне банка», то есть отдельный заказ оркестратора, а не слово
владельца. **Не берём: гейт — отдельный заказ.**
- **230** — зона «бэкенд + контракт». **Не берём: пак версию контракта не двигает** (см. ПОРЯДОК ДЕПЛОЯ).
- **Остальная свободная работа зоны в этот пак НЕ входит:** строки **246** (СРОЧНО — две копии полосы
отказов обещают разное), **247**, **233**, **229**, **141**-остаток, **131**. Из свободной очереди пак
берёт ровно **232** и **197** (`grep -n 'Свободная работа зоны' docs/PROGRESS.md``254`).
- **Следующий платный замер** — не в этом паке: у его оси «эффорт off против low» не разрешено
предусловие, и носитель предусловия назван — **строка 237(б)**, вендор-сверка маппинга эффорта
`deepseek-v4-pro` (`grep -n '^| 237 ' docs/PROGRESS.md``178`; реестр
`docs/architecture/13-tech-debt-anchors.md` §Б-108). По гардрейлу владельца закрывается ТОЛЬКО
вендор-докой. До решения владельца замер не заказывается.
## ⚠ ПОРЯДОК ДЕПЛОЯ — почему пак безопасен параллельно платформе
**Пак НЕ меняет форму манифеста, схему хранилища и версию контракта.** Понадобилось — **стоп и пинг**,
не «сделаю аккуратно». Почему это жёстко:
- **Форма манифеста БЕЗОПАСНОГО ПОРЯДКА НЕ ИМЕЕТ.** Гейт платформы — строгое равенство ОДНОЙ константе:
`if m.Version != KnownManifestVersion` (`grep -n 'if m.Version != KnownManifestVersion' platform/internal/ingest/manifest.go`
`112`; сама константа — `grep -n 'KnownManifestVersion = ' platform/internal/ingest/manifest.go`
`88`, `"tm-manifest-v2"`). Окна из двух версий НЕТ. Поэтому «платформа первой» так же смертельно, как
«движок первым»: выкатив любую сторону в одиночку, ты отправляешь КАЖДУЮ новую книгу в отказ
(`ReasonParserUnavailable`, который жжёт бюджет попыток). Бамп формы = **стоп-мир** либо специально
построенное временное окно двух версий. ⛔ **Рантбук зоны платформы советует обратное:**
`platform/deploy/README.md:213`=`сначала платформа, потом движок`. По коду этот совет опасен —
дефект чужой зоны, он передан платформенному паку; тебе он важен тем, что «платформа первой» НЕ
является безопасным обходом, если ты вдруг упрёшься в форму манифеста.
- **Схема хранилища — «движок первым»**, и это документировано: движок первым → `tmctl migrate` по
каждой книге до прогонов → и только потом платформа (D39.158 п.6,
`grep -n 'движок первым' docs/architecture/05-decisions-log.md``715`).
- **Версия контракта** — пак её не двигает. Твоё новое поле в `status --json` аддитивно и безопасно:
платформа читает отчёт аллоулистом (`ingest.StatusReport`, `runner/engine.go` «decodes the allowlisted
subset»), неизвестные поля игнорирует. Это ПРОВЕРЕНО, но проверь и ты — клейм «зоны независимы по
коду» держится именно на этом.
## Свобода и её разметка — сводка
Каждый пункт состава размечен внутри себя; здесь только свод, тело — выше.
| Пункт | «Делай РОВНО так» (отступление = пинг) | «Решаешь сам и аргументируешь» |
|---|---|---|
| 1 ФЧ-5 | четыре обязательства D39.150 п.1, включая снятие обещания точности в ОБОИХ живых местах | форма проводки текущей модели, формула округления вверх, слова оговорки |
| 2 строка 232 | ось = `finalStageWave`; закрытие объявляется только по остатку «слот»; остаток «первая редакторская стадия» — вне скоупа и НАЗЫВАЕТСЯ в отчёте | где вычитается потраченный слот, как переживает рестарт |
| 3 ФЧ-4 | обе половины; фигура едет с базисом (D39.181 п.1) | форма раскрытия на проводе и в тексте отказа |
| 4 ФЧ-1 | ровно три названные гарантии; запись в `mutations.json`; `tmmutate` только на копии | форма посадок, уровень тестов, текст `why` |
Плюс общее «ровно так»: **порядок пунктов** · **границы выше** · **три запрета из ПОРЯДКА ДЕПЛОЯ**.
Всё, что здесь не названо, — твоё решение с аргументом в отчёте. Мои приоры **опровергаются аргументом
или замером**; неопровергнутое исполняется как написано.
## Мандат самопроверки — исполнением
`make battery`**не отчёт, а предусловие** (проверено на этой машине: `cd backend && make battery`
EXIT=0, `golangci-lint` `0 issues`). Зелёная батарея НИЧЕГО не доказывает про твою работу — весь смысл
пункта 4 в том, что мутации её переживают.
**Обязательны, и каждое — с ПРЕДЪЯВЛЯЕМЫМ АРТЕФАКТОМ в отчёте:**
1. **Дифф `^func Test` исполнением, а не памятью.** Команда:
`grep -rn '^func Test' backend --include=*_test.go | wc -l` (на HEAD этой смены → `1083`) — и
пофамильный дифф имён до/после. **Удалённых имён должно быть 0**; если не 0 — назвать каждое и
объяснить.
2. **Собственные адверсариальные посадки ВНЕ трёх гарантий пункта 4** — механизм тот же `tmmutate` на
копии дерева. Артефакт: **красный вывод на каждую посадку**, и отдельно — список посадок, которые
батарея ПЕРЕЖИЛА (это и есть находки).
3. **Отдельный проход по СВОЕЙ ГОТОВОЙ работе свежим контекстом.** **Субагенты разрешены явно; модель
задавай ИМЕНЕМ и знай, сколько их у тебя работает** (Fable 5 — рекомендация одного-двух, не потолок;
веер под предмет выбираешь ты).
**Артефакт прохода — СПИСОК НАХОДОК С ДИСПОЗИЦИЯМИ в отчёте.** «Прошёл, находок нет» отчётом НЕ
является: ноль находок обосновывается тем, ЧТО ИСКАЛОСЬ и какими командами.
**Оси ревью, которые я называю уязвимыми ИМЕННО В ЭТОМ ПАКЕ** (13 хватит; **вправе заменить с
аргументом**, если найдёшь уязвимее):
1. **Направление ошибки.** Округление ВВЕРХ ложится на путь, где старое поведение занижало. Классика
этого места — **двойной счёт** (округлили и там, и там) и **перелёт порога согласия** (число выросло
настолько, что гейт спрашивает там, где раньше не спрашивал, или наоборот).
2. **`HistoricalRows` на новой границе** — не теряется и не удваивается; текст отказа и `--json` говорят
ОДНО И ТО ЖЕ число с одним и тем же базисом.
3. **Слот гранта на черновом-только конвейере**`finalStageWave` на нём равен `draft`; убедись
исполнением, что ось не выродилась в «никогда ничего не отгружено».
**Особо недоверчиво к `usd()`:** его собственный докстринг называет вывод принадлежности к поколению
«an inference, not an identity — **the one place in here worth distrusting**» и перечисляет ДВЕ
предпосылки, каждая из которых ломается (`sed -n '86,112p' backend/internal/pipeline/reprice.go`). Твоя
правка ложится прямо на него.
⚠ Запрет подгонки тестов — `CLAUDE.md` (D39.183). Край, который тебе понадобится: **этот промт
заказывает смену поведения в пунктах 1 и 3** (обещание точности снимается, текст отказа меняется), и
протухший под неё тест правится как обслуживание — но правка ОБЪЯВЛЯЕТСЯ в отчёте: что изменилось,
какой тест это описывал, куда уехала гарантия. Нет в промте и нет ратификации — **пинг**.
⚠ Перед отчётом **сверь каждый клейм с результатом инструмента ЭТОЙ сессии**. Последний абзац — план
или обещание? Сделай сейчас.
## Отчёт
**Записка-план ДО работы** — эхо-протокол старта, ≤10 строк «скоуп · инварианты · не-делать», в секцию
«Бэкенд» `docs/PROGRESS.md`. Первым действием, до кода.
**Комплектность против заказа — механически:** пройди по четырём пунктам состава и по таблице разметки и
отчитайся ПО КАЖДОМУ. ⚠ Эта тема трижды проваливалась на том, что исполнялся первый пункт, а нумерованный
остаток бросался.
**Итог — туда же, и «заявление = команда»:** каждое число и каждая категорика — **с командой получения**,
которая это число реально печатает. Приёмка пере-ранит твои команды; команда, печатающая «ok» вместо
числа, считается отсутствующей.
**Обязательная секция «Что НЕ удалось и что НЕ проверено».** ⚠ «Не проверено» ≠ «опровергнуто» — это
РАЗНЫЕ исходы, и **третий законен**: «взялся, не смог, вот где встал» — полноценный результат смены, а не
провал. Сюда же обязательно попадает остаток «первая редакторская стадия» (пункт 2) и любая гарантия
ФЧ-1, до которой не дошли руки.
**Конфликт промта с кодом или доками — ПИНГ через владельца, не интерпретация.** И у тебя есть **право
сказать «этого делать не надо»** с аргументом: сессия, приносящая развилку вместо тихой девиации, делает
правильно. Этот промт писан по коду на HEAD своей смены; код первичен, и если ты видишь другое — это
находка, а не твоя ошибка.
## Прямой канал
Механизм целиком — `CLAUDE.md`, «Связь между сессиями»: свой блок в `/tmp/textmachine-channel`, сверка
с `ListAgents` перед отправкой, канала нет — нормальный случай, сессии подряд не опрашивать. Что
добавляет этот пак: **эхо-записку отправь ПЕРВЫМ действием по адресу оркестратора** — это и есть
проверка канала.