textmachine/docs/BACKEND_BOUGHT_MEANS_DELIVERED_SESSION_PROMPT.md

566 lines
65 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.

# ⟶ ПЕРЕДАЧА ПАКА 15.09 (ред. 5) — ЧИТАЙ ЭТО ПЕРВЫМ
> Оркестратор №23; прежняя сессия оркестратора `textmachine-11` оборвалась, адрес нынешней — в канале (П8).
> Пак $0. **При расхождении этот раздел СИЛЬНЕЕ тела ниже** (§1§13, редакция 4); прямые отмены тела перечислены
> в П6. Тело остаётся описанием ПРЕДМЕТА: что заказано, почему и чем мерить.
## П1. Что случилось и что решит твой результат
Пак выдан 11.09 сессии `textmachine-b1`. Она построила его почти целиком — 22 изменённых и 4 новых тестовых файла
в `backend/` (+2850/158), плюс отчёт в `docs/PROGRESS.md`; всего 27 путей, +3215/158 — провела семь кругов
адверсариального чтения и потерялась при переезде окружения, **не объявив работу завершённой**. ⇒ пак идёт,
исполнителя у него нет, и слот бэкенда занят, пока пак не закрыт актом.
**Твой результат — довести ЧУЖУЮ почти готовую работу до критерия §13 и сдать на приёмку.** Не перестраивать и не
улучшать сверх заказа: предмет решён владельцем и построен.
**Почему это раздел того же промта, а не отдельный пак** (эррата 04.09-г в шапке
`docs/architecture/05-decisions-log.md` велит круг над ГОТОВЫМ паком давать отдельным паком следующей сессии). Здесь
пак НЕ готов — строки завершения нет, — и слот зоны один; копировать тело заказа в новый файл значило бы завести
второй носитель того же предмета. Свежий глаз, ради которого эррата написана, у тебя есть по построению.
По критерию §13 открыто СЕМЬ вещей — замерено мною 15.09, команды рядом:
1. **Круги не сошлись.** Седьмой круг (узкий, по починкам шестого) дал два мажора — С1 и С2, оба починены, но круга,
проверившего эти починки, нет. Что круги находят дефекты в починках предыдущих, отчёт говорит о кругах 23 сам;
у 47 это видно по их таблицам.
2. **Секции «Числа» нет,** хотя отчёт ссылается на неё трижды. Заголовков в секции b1 — 31, ни одного про числа:
`awk '/^#### Пак «КУПЛЕНО/{f=1} f&&/^#### Пак/&&!/КУПЛЕНО/{exit} f' docs/PROGRESS.md | command grep -c '^#'`.
3. **Таблицы мутаций нет** — §13 требует полную, выжившие поимённо.
4. **П.9 раздела «Что НЕ удалось» протух:** говорит о четырёх кругах, их семь.
5. **Подозрение b1, оставленное непроверенным:** `repin.go` может дропать помеченного члена c-lite юнита (её м7 и
«Что НЕ удалось» п.8). Проверить исполнением или назвать с доводом, почему вне пака.
6. **Нет строки «работа завершена, править не планирую».**
7. **Четыре якоря в ТВОЕЙ зоне сдача b1 сдвинула:** `backend/docs/DISCLOSURE_LAW_DESIGN.md:4144` (цели — `status.go` ×3 и
`bookbuild.go`). Перенавести по ЦЕЛИ: найти, куда уехало то, о чём говорит проза, а не подобрать токен под то, что
оказалось на строке (гардрейл `CLAUDE.md`). Хук покажет в этом файле ПЯТЬ битых якорей: пятый, `:40`
(`quality.go`), был сломан ещё в `HEAD`, до сдачи. Перенаведи и его тем же способом, но в число сдачи не записывай.
Ещё 17 якорей в `docs/` сдача тоже сдвинула — их перенаводит оркестратор при лендинге, не трогать.
## П2. Где лежит работа — и запрет, который здесь дороже обычного
- Сдача b1 лежит **ЗАСТЕЙДЖЕННОЙ В ИНДЕКСЕ** рабочего дерева `/home/ubuntu/projects/textmachine-main`: 27 путей
(22 M и 4 A в `backend/`, плюс `docs/PROGRESS.md`). Незастейдженных правок в этих путях нет.
- **Проверка целостности** (порядок — П4.1): `git -C /home/ubuntu/projects/textmachine-main diff --cached --binary |
sha256sum` обязан дать `166997963c95db52f0ba04716017fa2e106ae0596886e29cc918a642afe7df8a`. Бэкап того же патча —
`/home/ubuntu/tm-b1-orphan-index.patch`; копия сдачи ТОЛЬКО ДЛЯ ЧТЕНИЯ — `/home/ubuntu/tm-b1-orphan` (дерево
`5dbb5cb` с наложенным патчем; корпуса книг и твоих правок в ней нет).
- **Авторство видно git'ом:** `git diff --cached -- backend/ docs/PROGRESS.md` — работа b1;
`git diff -- backend/ docs/PROGRESS.md` — твои правки поверх неё. Твои НОВЫЕ файлы не видны ни тем, ни другим —
перечисли их в отчёте: `git status --short --untracked-files=all -- backend/`.
- ⛔ **ИНДЕКС НЕ ТРОГАТЬ:** `git reset` · `git restore` (и `--staged`) · `git stash` · `git checkout -- <путь>` ·
`git add` · `git clean` — запрещены. Одно неверное движение стирает работу, у которой нет второго исполнителя.
- **Ты не коммитишь.** Зона — `backend/` плюс продолжение секции b1 в `docs/PROGRESS.md` (место — П5).
- Чужие незакоммиченные файлы вне этого пака не трогай.
## П3. Карта чтения — ЗАКОН
1. `CLAUDE.md` — целиком.
2. Этот промт — раздел ПЕРЕДАЧИ и тело §1§13. ⚠ `file:line` тела указывают в `HEAD` (`5dbb5cb`), а не в дерево
сдачи: адреса уехали (`stagerun.go:204` → `:216`, `:142` → `:160`, `cutcall_test.go:402` → `:451`;
`resolvedForResume` уехал ниже в `cutcall.go` и делегирует `FlagReason.AnswersForResume` в `disposition.go`, а
прежняя строка `FlagReason(cs.FlagReason) != FlagCancelled` осталась только в `cutoververdict_test.go`). Читай
тело через `git show HEAD:<путь>` либо греп-токеном, а не номером. Ссылки тела §3 (`D39.204`, `D2` п.2, ряд 291,
кодовые файлы) — справочник предмета, открывай их по мере вопроса, в счёт карты они не входят.
3. Отчёт b1 — `docs/PROGRESS.md`, от `#### Пак «КУПЛЕНО — ЗНАЧИТ ДОСТАВЛЕНО»` до следующего `#### Пак`. Сперва:
«Что построено», седьмой круг, «Что НЕ удалось», «Объявляю правки», «Вопросы оркестратору».
4. Сама работа: `git diff --cached --stat` и `git diff --cached -- backend/`.
5. `docs/architecture/12-go-style-notes.md` — только больные места.
## П4. Что делать — разметка свободы
**Делай РОВНО так** (отступление — пинг):
1. **Первые действия, в этом порядке:** блок в `/tmp/textmachine-channel` → проверка целостности (П2) → эхо (тело §9)
СВОИМИ словами. Хеш не совпал — стоп: до любой правки таблица «ожидал / получил» пингом, а если канала нет —
секцией в отчёт, и дальше не идёшь. Отдельной строкой эха: что ты считаешь недоделанным СВЕРХ моих семи пунктов.
2. **Базовая линия в ТВОЁМ дереве до первой правки:** `make battery` из `/home/ubuntu/projects/textmachine-main/backend`
(git он не трогает, пишет только в `backend/bin`, корпус книг только читает). ⚠ Мой замер на копии без корпуса
дал 9 скипов; в главном дереве корпус есть, и по коду должны побежать ещё пять корпусных пинов
(`TestCheckerLabelsCandidates` · `TestCheckerLabelsBaseline` · `TestK6LabelsBaseline` ·
`TestK6TheTwoRelaxationsPricedSeparatelyOnTheSameLabels` · `TestTheInstrumentAgainstTheColdRunOracle`), так что
скипов ожидается 4: `TestMinerFullBookParity` (нет словаря jieba) · `TestCorpusBankKeyConflicts` (не задан
`TM_CORPUS`) · `TestHelperEventsRun` и `TestHelperKillLoop` (вспомогательные процессы). Это вывод из кода, не
прогон: другое число — не регрессия, но назови его.
3. **Сверка «отчёт ↔ код» в ОБЕ стороны.** (а) Каждая строка таблиц «Что построено» и седьмого круга (С1С6) — правка
ЕСТЬ в коде. (б) Каждый ханк `git diff --cached -- backend/` (75 ханков в 26 файлах) — у него ЕСТЬ строка отчёта
(таблицы кругов, «Объявляю правки» п.127). Ханк без строки — находка «незаписанная работа», строка без ханка —
«записанная, но не сделанная». Обе — в отчёт, не в молчаливую починку.
4. **Мутации — только на КОПИИ.** ⛔ `-root`, указывающий на `/home/ubuntu/projects/textmachine-main/backend`,
запрещён: `tmmutate` восстанавливает байты, прочитанные в момент посадки, и затрёт твою правку в том же файле, а
убийство процесса по памяти оставит посадку в дереве, которое уедет в лендинг.
- подмножество гейта — `make mutations` из `backend/` (229 записей из 440);
- **полный каталог** — рецепт цели `mutations` из `backend/Makefile`, развёрнутый в shell, без `-battery` и с
`-logs` ВНЕ копии (`trap` удаляет копию на выходе вместе со всем, что внутри). Проверено мною 15.09 исполнением:
запись `Z-price-substitution-reports-itself-as-the-answerer` — тем же рецептом встроенной командой (логи в
скретчпаде): копия без `.env`, RED; запись `C-verdict-leaves-the-anchored-column` — ДОСЛОВНО этим текстом как
файлом-скриптом (`<тег>` подставлен, добавлен `-id`): RED двумя тестами, один из них корпусной оракул
`TestTheInstrumentAgainstTheColdRunOracle` (ветка двух корпусных переменных на копии работает), лог вне копии,
`TMMUTATE_EXIT=0`, оставленных копий 0, индекс цел. ⚠ `<тег>` замени: с ним как есть `bash -n` падает. Положи в файл вне копии и запусти фоном средствами
харнесса, `bash <файл> > <лог> 2>&1; echo "TMMUTATE_EXIT=$?" >> <лог>`, и жди строки `TMMUTATE_EXIT=` в логе, а не
уведомления:
```
cd /home/ubuntu/projects/textmachine-main/backend && test -f go.mod \
&& [ "$(pwd)" = /home/ubuntu/projects/textmachine-main/backend ] \
&& logs=/home/ubuntu/tm-mut-logs-<тег> && mkdir -p "$logs" \
&& tmp=$(mktemp -d) && trap 'rm -rf "$tmp"' EXIT && trap 'rm -rf "$tmp"; exit 130' INT TERM \
&& mkdir "$tmp/backend" \
&& tar -C /home/ubuntu/projects/textmachine-main/backend -cf "$tmp/tree.tar" --exclude='./.env*' --exclude=./bin . \
&& tar -C "$tmp/backend" -xf "$tmp/tree.tar" && rm -f "$tmp/tree.tar" \
&& TM_CHECKER_LABELS_DIR=/home/ubuntu/projects/textmachine-main/books/gu-zhenren/labels \
TM_RUN_A_DIR=/home/ubuntu/projects/textmachine-main/books/gu-zhenren/door-to-file \
go run ./cmd/tmmutate -root "$tmp/backend" -logs "$logs"
```
Рецепт из шапки `cmd/tmmutate/main.go` как есть НЕ годится: без двух корпусных переменных корпусные пины на
копии скипают, скип даёт `exit 0`, и посадка читается выжившей;
- одна копия — один мутатор; склейка из нескольких вызовов `-id` законна только при покрытии 440 из 440 без
пересечений И при том, что между вызовами дерево не менялось (твоя правка между ними делает склейку негодной);
- исходы `tmmutate`: `RED` — поймана · `SURVIVED` — выжила · `survives` — ЗАПИСАННАЯ и аргументированная выжившая
(в каталоге 4 такие записи с `expect: survives`, все без `battery`, поэтому видны ТОЛЬКО в полном прогоне — в
таблицу отдельной строкой, не в выжившие) · `NOTHING` и `ROTTED` — НЕИЗМЕРЕННЫЕ (в число не кладутся) · `RED!` —
запись числилась выжившей и теперь ловится · «NOT GREEN before any mutation» — прогон не состоялся вовсе.
Засчитывает мутацию ТЕКСТ падения из `-logs`, а не цвет. Время: полный каталог — больше часа.
5. **Секция «Числа» и таблица мутаций** — написать; каждое число с командой, деревом и популяцией.
6. **П.9 «Что НЕ удалось»** — исправить с пометкой «испр. 15.09».
7. **Финальная строка §13** — по исходу правила остановки (ниже) или по сходимости.
**Решаешь сам и аргументируешь** (советы — приоры, опровергаются аргументом или замером):
- **Форма восьмого круга.** Узкий круг, вернувшийся чистым, доказывает чистоту ПОСЛЕДНИХ починок, а не сходимость
пака. Норма (эррата 04.09-б): круг идёт по дофиксу **против ИСХОДНЫХ пунктов заказа** — здесь дофикс чужой, и
тем важнее. Мой приор: один направленный читатель по С1С6 плюс проход по пунктам тела §4.1§4.5 против готового
дерева. Субагенты разрешены явно; веер и глубину выбираешь ты.
⛔ **Правило остановки — РОВНО так; сам решаешь только, чинить ли найденное до стопа.** Кругов в передаче не больше
ДВУХ. Стоп наступает, если (а) любой круг нашёл дефект в починке ПРЕДЫДУЩЕГО круга (седьмого или своего) либо (б)
второй круг дал любую НОВУЮ находку. В обоих случаях — таблица «находка → класс → цена» в отчёт и пинг, если канал
жив; если канала нет — та же таблица секцией. Финальная строка в этом исходе: **«работа завершена по правилу
остановки П4, круги НЕ сошлись — остаток в таблице; править не планирую»**. Лендить ли с названным остатком, решаю я
(прецедент — `D39.245` п.1).
## П5. Где эта передача мягкая — и куда писать
**Уязвимые места:**
1. ⛔ **Чужие числа.** Все числа отчёта сняты приборами b1 на деревьях своего часа: «каталог 439 записей», «25 = 9 +
3 + 1 + 12», «батарея 23/23» — утверждения автора, а не твоё знание. Её же командой ты повторишь и её ошибку:
бери другую популяцию или другую сторону улики.
2. ⛔ **Работа, оборванная на полуслове.** Самая вероятная форма дефекта — починка с одной половиной: комментарий
переписан, а пин не заведён; запись каталога заведена, а `find` не уникален; пин заведён, а посадка бьёт не в
его слой. У каждой починки седьмого круга спрашивай, где её вторая половина.
3. ⛔ **Изменённые записи каталога, а не только новые.** Кроме 33 новых, сдача изменила 8 записей `HEAD`: у шести —
`find` (у одной из них ещё и файл), у двух (`CUTCALL-a-refusal-pays-when-the-reply-outruns-the-write` ·
`CUTCALL-a-reply-that-outruns-our-own-write-books-zero`) — `run`, расширенный на
`TestDeliveryEvidenceIsWhatTheMoneyBitAsks`. Значит, RED посадки §4.5(б) больше не доказывает, что её ловит
СКВОЗНОЙ пин — детерминированная таблица поймает за него. Назови по логу, какой тест краснеет.
4. ⛔ **Решения прежнего оркестратора, известные только из отчёта b1.** Переписки с ним нет. Что я подтверждаю и что
решаю сам — П6; прочее ратифицированным не считай: наткнёшься — пинг.
5. ⛔ **Зелёная батарея плюс полный каталог сходимостью не являются** — семь кругов этого пака находили дефекты при
полной зелени.
**Куда писать в `docs/PROGRESS.md`.** Новые подсекции — в КОНЕЦ секции b1, то есть сразу после её
«Вопросов оркестратору» и перед следующим `#### Пак`: сперва «ПЕРЕДАЧА 15.09 — план» (замена тела §7, до первой
правки), затем круги, «Числа», таблица мутаций, «что не удалось» по передаче. Прежние круги b1 не переписываются
(они описывают дерево на свою дату); протухший п.9 правится на месте с пометкой. Строка завершения ставится в
ЗАГОЛОВОК секции b1 — так, как у следующей в журнале секции `#### Пак «ЗА ОДНУ ЕДИНИЦУ ПЛАТИМ ДВАЖДЫ»`. Хук якорей при
лендинге предупредит о семи якорях отчёта b1 без токена: дописать к ним `=`токен`` РАЗРЕШЕНО, текст не трогая, и
токен подбирается по ЦЕЛИ, о которой говорит проза, а не по тому, что оказалось на строке.
## П6. Отмены тела, ответы на вопросы b1 и мои решения
**Отменено в теле (раздел сильнее). Носитель — «Эррата 15.09-а» в шапке `docs/architecture/05-decisions-log.md`:**
- Слово владельца 11.09 (цитата — тело §4.1): отказ резервации на ретрае — СТАНДАРТНАЯ ОСТАНОВКА. У `D39.204` сняты:
в п.4 — деградация во флаг, отгрузка текста и продолжение волны; п.5 (а)(б); в п.5 (в) — пин «купленное
доставлено ⇒ не `exit 4`». ОСТАЁТСЯ требование п.4, чтобы причина отличалась от исчерпания регенераций, — на нём
стоит `retry_unaffordable` и комментарий сдачи, ссылающийся на «D39.204 п.4». По этим расхождениям тела с журналом
не пинговать.
- Тело §2, фраза «её половина едет ОТДЕЛЬНЫМ промтом», и последнее предложение §4.4 («я передам это платформенной
сессии, и акт будет один») — сняты: платформенная половина пуста по замеру b1 (§4.4 её отчёта), отдельного
платформенного промта не будет, акт один. Обязательство ряда 291 «зеркало двух зон» закрывается этим замером.
- Тело §7 — заменено (П5). Пункты «решаешь сам» §4.1 уже решены b1 и без находки пере-решению не подлежат.
**Ответы на «Вопросы оркестратору» b1:**
1. Ряды контракта 14 — их не хватает ТРИ (`retry_unaffordable` · `cancelled` · `attempt_timeout`; канон говорит
«Причин шестнадцать», в `flagSeverity` 19 ключей) — моя зона, делаю при лендинге. Не трогать.
2. Расхождение витрин `status` и экспорта на одной строке — ряд бэклога заведу я при лендинге.
3. Вторая дверь невидимой дыры (позиция, чья единственная трата — сожжённый ключ, метки не получает) — вопрос
владельцу, несу я. В этом паке не строится.
4. Третья читательская фраза («оплачено, но не доделано: пополните и перезапустите») — вопрос владельцу, несу я.
Ветку не трогать: она готова принять строку данных.
5. Ось «эхо только по отгруженному тексту» (`D39.18`, открытое решение владельцу) — абзац «чтение, которое берёт код»
оставить как есть.
6. Переименование `echo_edit_units` → `echo_edit_rows` — **в этом паке НЕ делается:** правка опубликованного ключа
потребовала бы нового круга по пакету, который ты сводишь, а читателей вне `backend/` у ключа ноль и на исполнимых
конфигах числа совпадают. Оговорка у поля остаётся, ряд бэклога заведу я.
7. Анкор техдолга в `docs/architecture/13-tech-debt-anchors.md` — моя зона.
**Три просьбы b1 к оркестратору, которых нет среди её «Вопросов», — исход:**
8. Смена смысла эхо-метрики на эдит-волне (её Т1 и «Что НЕ удалось» п.7) — **решаю при приёмке**, сессия не трогает.
9. Расщепление `withheld_units` на «нужны деньги» и «нужен человек» (её М5) — контракт двух зон; ряд бэклога заведу я.
10. Ранг новой причины 4 вместо 8, стоящий на существующем пине `TestTheSeverityTableMeansWhatItsCommentsSay` —
**решаю при приёмке**, сессия не пере-решает.
**Подтверждаю** решения прежнего оркестратора, на которых стоит построенное: (а) новый РОД дыры не заводится — довод
о пяти счётчиках платформы (`platform/internal/ingest/build.go`); (б) выбор читательской фразы спрашивает предикат
вердикта, а причина остаётся на записи дыры; (в) платформенная половина пуста — см. отмены выше.
## П7. Среда этой машины (15.09, а не прошлой)
- `/tmp` здесь — ext4 на общем диске, не tmpfs. Место проверяй командой `df -h /tmp` рядом с итогом тяжёлого
прогона, а не числом отсюда: `no space left on device` даёт красное, неотличимое от дефекта кода.
- Тулчейн: go 1.26.7, golangci-lint 2.12.2 (пин — 2.12.2).
- ⛔ Перед тяжёлым прогоном — `cat /proc/self/cgroup` и `memory.max` этого cgroup (гардрейл `CLAUDE.md` про
`CONSTRAINT_MEMCG`). У моего процесса потолка нет, у твоего может быть. Сужать мутационный прогон через `-run`,
чтобы влезть в память, нельзя.
## П8. Остальное — по телу
§5 · §6 · §8 · §9 · §10 · §11 (канал вопросов, право отказаться, тесты не под зелень, старший коллега Fable 5 — один
на сессию) · §12 · §13 действуют и адресованы тебе, с заменами из П4П6. **Адрес оркестратора** — блок
`role=оркестратор` в `/tmp/textmachine-channel`, сверенный с `ListAgents`; ⚠ `textmachine-main-b1` в списке — пустая
сессия владельца, а НЕ сессия b1, и сессия `textmachine-11` из шапки тела мертва. Нужной роли в файле нет ⇒ канала
нет: вопросы секцией отчёта, работа продолжается (исключение — стоп по хешу, П4.1).
---
# Бэкенд-пак: КУПЛЕНО — ЗНАЧИТ ДОСТАВЛЕНО. Юнит, который не доделается НИКОГДА
> Выдан оркестратором №23 (сессия `textmachine-11`) 11.09. Пак **$0** — платных вызовов в нём нет.
> Предмет решён владельцем 05.09 (`D39.204`); ты строишь решённое, а не выбираешь между вариантами.
## 1. Какая проблема и что решит твой результат
Читатель заплатил за главу. Движок купил первую попытку, получил флагуемый результат, пошёл за второй — и
**вторую ему не продали**: потолок книги или дня не вмещает резерв регенерации. ⚠ **И тут точность несущая,
первая редакция промта её теряла:** `maxTokensForAttempt` удваивает **только ВЫХОДНОЙ** бюджет, а резерв
считается оценкой по цене, промпту, потолку и буферу размышления (`backend/internal/pipeline/stagerun.go`,
греп `EstimateUSD`) — промпт и аддитивный буфер НЕ удваиваются. Значит «попытка 1 стоит ×2» — неправда, и
окно, в котором отказ неизбежен на ≥1 и невозможен на 0, считается по РЕАЛЬНОЙ оценке.
Дальше происходит вот что, и это проверяется одной строкой: **`backend/internal/pipeline/stagerun.go:204`=`infra failure; the cancelled-position mark`** — ЛЮБАЯ ошибка попытки уходит наверх из цикла, **РАНЬШЕ `UpsertChunkStatus`**.
**Следствия два, и оба стоят денег читателя:**
1. **Юнит не доделается НИКОГДА.** Чекпойнт попытки 0 есть (реплей за $0), строки `chunk_status` НЕТ
читающие модели видят юнит как `pending`, экспорт не отдаёт ничего. Каждый резюм реплеит попытку 0 за
$0 и умирает на попытке 1. **Прогон уходит `exit 4` при ПОЛНОСТЬЮ доставленных купленных юнитах.**
2. **Волну нельзя продолжить без ДЫРЫ в непрерывном префиксе** — то есть за это платят и соседние юниты,
которые в бюджет влезали (разброс резервов внутри волны замерен ×2.976).
**Решение владельца (`D39.204`, и оно НЕ обсуждается):** отказ резервации на РЕТРАЕ **деградирует во ФЛАГ**,
причина которого ОТЛИЧНА от исчерпания регенераций; текст попытки 0 оплачен и **отгружается**; волна идёт
дальше. ⛔ Вариант «минимальная покупка ≥ `step_max_usd`» **СНЯТ** владельцем — он стоил бы покупателю
входного порога в пять глав. Не реализуй его и не предлагай.
⚠ **И канон здесь сильнее, чем кажется:** `D2` п.2 уже ТРЕБУЕТ тегировать `flag_reason`. Значит новая
причина — не добавка к контракту, а его ИСПОЛНЕНИЕ. (Ссылка «смена `D2.3`», гулявшая по бэклогу, **неверна
дважды**: `D2` и так кончается флагом, а `D2.3` — про детектор вырождения перед удвоением.)
## 2. Зона записи и git
- Зона — **`backend/`**, и только она. `platform/` не трогать: её половина едет ОТДЕЛЬНЫМ промтом, но
**ОДНИМ АКТОМ с твоим** (см. §4.3).
- **Ты не коммитишь.** Готовое дерево передаёшь мне, я лендлю. `git add -A` запрещён каноном.
- Свою секцию итогов пиши в `docs/PROGRESS.md` — это единственное исключение из «`docs/` — зона
оркестратора». Чужие незакоммиченные файлы в дереве не трогай.
## 3. Карта чтения — ЗАКОН, дальше только по её ссылкам
1. `CLAUDE.md` — целиком.
2. **Тело `D39.204`** в `docs/architecture/05-decisions-log.md` (решение владельца) и **`D2` п.2** — он в
закрытой эре, тело в `docs/archive/architecture/05-decisions-D1-D38.md`, греп `^## D2\.`. ⚠ Подномер
собственного тела не имеет: `D2.3` — это ПУНКТ 3 тела `D2`.
3. **Ряд 291** в `docs/BACKLOG.md` — там весь разбор, включая **опровергнутую посылку прошлого
оркестратора**: «ретрай оставляет ФЛАГОВАННЫЙ юнит» — ЛОЖЬ, зона опровергла по строкам. Не наступи
второй раз.
4. `backend/internal/pipeline/stagerun.go` (цикл попыток и `runAttempt`), `escalation.go` (`errReserveCeiling`
и кто на нём УЖЕ деградирует), `disposition.go` (словарь `FlagReason`).
5. `docs/architecture/12-go-style-notes.md` — нормативы стиля, только больные места.
Код-факты добывай сам; `file:line` выше — отправные точки. **Код первичен.**
## 4. Состав
### 4.1 ⛔ КОНСТРУКЦИЯ ПЕРЕ-РЕШЕНА ВЛАДЕЛЬЦЕМ 11.09 — читай это ПЕРЕД кодом
**Слово владельца дословно:** «Надо о таком просто сообщать в виде метаданты, которая сейчас и пишется
предупреждением, и иметь возможность поднять просто потолок и возобновить прогон, так как **это ситуация
всё же СТАНДАРТНАЯ СО СТОПОМ, просто он произошёл по какой-то другой причине**».
⛔ **Это отменяет обе половины прежнего заказа — и моего, и `D39.204` п.4.** Ни «деградировать во флаг», ни
«волна идёт дальше» больше НЕ строится. Прежний план заводил новый исход; новый — **возвращает ситуацию в
УЖЕ ПОСТРОЕННЫЙ штатный путь остановки**, из которого она сегодня выпадает.
**Что это значит механически (моё чтение; спорь, если код скажет иначе):**
1. **Прогон ОСТАНАВЛИВАЕТСЯ, как при любом отказе по потолку.** Не продолжается с флагом. ⭐ И это заодно
снимает то, чем ряд 291 пугал: «продолжить волну нельзя без ДЫРЫ в непрерывном префиксе» — при остановке
дыры не возникает вовсе.
2. **Отказ ретраю публикует ту же метадату, что и любая остановка по потолку** — кадр
`Ceiling{Halted, Scope, ShortfallMicroUSD}` (`backend/internal/runevents/runevents.go`, греп
`ShortfallMicroUSD`). ⭐ **Он уже несёт ровно то, что владелец просит:** «пополни хотя бы на столько, и
тот же вызов будет допущен» — и это **ратифицировано им же, `D39.203`**. Строить новый носитель не надо.
⚠ **И твоя находка №2 этим РАЗРЕШАЕТСЯ, а не отменяется:** ты была права, что `ceilingReached`
идемпотентен по прогонному флагу и съел бы событие настоящей остановки — но теперь съедать НЕЧЕГО: эта
остановка И ЕСТЬ настоящая. Публикуй через штатный путь.
3. ⛔ **НАСТОЯЩИЙ ДЕФЕКТ — УЖЕ, чем я написал, и он назван в самом дереве.** Механизм «отметить
остановленную позицию» построен ИМЕННО против невидимых дыр:
`backend/internal/pipeline/stagerun.go:142`=`THE MARK FOR A STOPPED POSITION IS ATTACHED TO EVERY EXIT`,
и его комментарий приводит замер прежнего инцидента — «прогон, остановленный на эскалационном хопе,
рассчитал деньги и не оставил строки вовсе: `committed=0.001176`, `chunk_status_rows=0`». **Но метка —
no-op для всего, что не «доставленный вызов, который остановил человек»** (`recordCancelledStage`).
Отказ по потолку под это не подпадает ⇒ юнит остаётся без строки. **Вот дыра, и вот что ты закрываешь.**
4. **Поднял потолок → резюм ДОДЕЛЫВАЕТ юнит.** Значит строка, которую ты пишешь, обязана быть
НЕ-ТЕРМИНАЛЬНОЙ для резюма — по образцу `FlagCancelled`, единственной причины, у которой
`resolvedForResume` ложен (`backend/internal/pipeline/cutcall.go:293`=`FlagReason(cs.FlagReason) != FlagCancelled`),
и ложен именно потому, что это «оплачено, но не сделано». Твой случай той же природы.
**Делай РОВНО так:** остановка штатная · метадата через уже построенный кадр с недостачей · **юнит получает
строку, а не пустоту** · строка НЕ терминальна для резюма · после пополнения резюм доделывает.
**Решаешь сам и аргументируешь:** как назвать причину в строке юнита (или взять существующую) · где ставится
метка · как отличить «остановлено на ретрае» от «остановлено на попытке 0» в носителе, и надо ли отличать.
⛔ **Попытку 0 не трогать:** её отказ и сегодня останавливает книгу штатно — ратифицировано.
⛔ **КТО ПЛАТИТ — НЕ ПРЕДМЕТ ЭТОГО ПАКА.** Слово владельца 11.09: «пока платит за всё ЮЗЕР, потом будем
решать эту проблему». Механизм «платит продукт» не строить; существующее биллинговое поведение не трогать
ни в какую сторону, даже где оно выглядит непоследовательным. ⚠ И отдельной строкой отчёта НАЗОВИ (не чини):
объёмный леджер сегодня считает флагованный юнит НЕ доставленным, то есть грант за него не съеден — это
расходится со словом владельца и существует ДО твоего пака. Он объявил вопрос отложенным.
⚠ **Отгрузка обрезанного текста не строится** — `D2` запрещает отгружать загрязнённое, и это не зависит от
того, кто платит.
### 4.2 Пин, ради которого пак существует
**«ОСТАНОВКА, КОТОРАЯ ЛЕЧИТСЯ ПОПОЛНЕНИЕМ».** Три утверждения, и они про разное:
1. **Юнит, на ретрае которого отказал потолок, ПОЛУЧАЕТ СТРОКУ.** Сегодня её нет — это и есть невидимая
дыра. Пин обязан печатать величину, доказывающую, что деньги были потрачены, а строка появилась: пустой
`chunk_status` рядом с ненулевым `committed` — ровно тот замер, которым дерево описывает прошлый
инцидент.
2. **Остановка несёт НЕДОСТАЧУ.** `ShortfallMicroUSD` ненулевой и таков, что пополнение на него допускает
тот же вызов. ⚠ Пин на «событие вообще есть» вырожден — утверждай ЧИСЛО.
3. **Пополнил потолок → резюм ДОДЕЛАЛ юнит.** ⛔ **И это ГАРД, а не доказательство лечения — поправка
исполняющей сессии, принята.** Моё «сегодня второй прогон умирает вечно» НЕВЕРНО: строки у юнита нет ⇒
гейт резюма не срабатывает ⇒ позиция ре-атакуется, попытка 0 реплеится из чекпойнта за $0, ретрай
покупается, и при поднятом потолке юнит доделывается **уже сегодня, без пака**. Вечно умирает только
резюм БЕЗ пополнения. ⇒ этот пин стережёт ратифицированное `D4`, чтобы пак его не сломал, и **на дефекте
он ЗЕЛЁН**. Так и напиши.
⛔ **И отсюда норма приёмки этого пака, которой у нас ещё не было ни в одном:** сними ЦВЕТ КАЖДОГО из трёх
пинов на дереве **ДО** фикса, числом, и положи замер в отчёт. На дефекте краснеет только первый. «Пин
зелен» и «пин стережёт» — разные утверждения, и без этого замера они сливаются.
⚠ **Про коды выхода.** Прежняя редакция промта требовала `exit 2` вместо `exit 4`. **Снято:** при штатной
остановке код остаётся тем, каким он и был для остановки по потолку. Предмет пака — не смена кода выхода, а
то, что за ним стоит: **строка вместо пустоты и лечимость пополнением.**
⚠ **Два гейта поймают тебя сами — назову, чтобы не терять круг:** `TestEveryFlagReasonIsRanked` потребует
завести новую причину в `flagSeverity` (`backend/internal/pipeline/status.go`), если ты её заводишь, а
`TestEveryOperatorMessageIsCatalogued` — внести новое сообщение в
`backend/internal/pipeline/testdata/operator-messages.txt` (сегодня в каталоге 134 строки, из них о потолке
уже говорят четыре — посмотри их прежде, чем писать пятую).
### 4.3 ⛔ ДЕФЕКТ, КОТОРЫЙ ТВОЙ ПАК НАКОРМИТ — и он НЕ латентный. Чинить ЗДЕСЬ
Нашла исполняющая сессия, посылку я пере-проверил и **она у неё неверна в СТОРОНУ МЯГКОСТИ**.
`backend/internal/pipeline/quality.go` (греп `draftEchoRecovered`) считает эхо ВОССТАНОВЛЕННЫМ по одному
признаку — `FirstFlagReason == cjk_artifact`, — **не спрашивая, флагована ли строка сейчас и чем**. Денежный
отказ эхо-рероллу даст ровно эту картину: первый флаг — эхо, итоговый — денежный, реролла не было, а отчёт
скажет «восстановлено».
⛔ **Сессия назвала это латентным «потому что ключ 0». Ключ НЕ ноль:** `regenerate_echo_before_escalate: 1`
стоит **во всех ЧЕТЫРЁХ боевых конфигах** (`backend/configs/pipeline-c1.yaml`, `-c2`, `-arm-glm`,
`-arm-mistral` — сверено мною по каждому). ⇒ дефект оживает **в день лендинга твоего пака**, а не когда-то
потом. **Чини его в этом паке и пинь** — «диспозиция в отчёте» здесь недостаточна.
### 4.4 Платформенная половина — **твоё дело назвать, не построить**
Карта заметок платформы **РУКОПИСНАЯ** (`platform/internal/ingest/notes.go`) и уезжает независимо от
словаря движка. Цена несделанного **замерена**: `PD-246` — минор 0.10.0 сутки жил ЛОЖНЫМ на проводе, причина
доезжала читателю как `unspecified`, и поймала это зона своей пере-проверкой ПОСЛЕ слов оркестратора
«приёмка закончена».
⇒ **отдельным пунктом отчёта** назови мне: точное значение новой причины на проводе · что платформа обязана
записать прогону, который кончился флагом вместо `exit 4` · какой её пин это стережёт. Я передам это
платформенной сессии, и **акт будет один на две половины**.
⚠ **И следствие, которое стоит назвать сразу, потому что оно про деньги читателя:** объёмный леджер считает
флагованный юнит НЕ доставленным (`backend/internal/pipeline/volume_test.go`, греп
`TestAFlaggedUnitIsNotReportedAsDelivered`). Значит грант за него не съеден, а книга честно короче — это
хорошая новость, но она обязана быть СКАЗАНА в отчёте, иначе следующая смена откроет её заново.
### 4.5 Приложение — ДВА пина, краснеющих по УДАЧЕ (ряд 379, ОБЕ половины)
Отдельная от §4.1 работа; делай её ПОСЛЕ основной. ⛔ **И мой диагноз здесь был НЕВЕРЕН — опровергнут
замером, а не мнением. Читай, чем именно, иначе повторишь мою ошибку.**
**Половина (б).** Запись каталога `CUTCALL-a-refusal-pays-when-the-reply-outruns-the-write` стережёт самую
дорогую половину денежного бита `delivered()`
(`backend/internal/llm/attemptcut.go:189`=`answered && t.firstByte.Load()`): провайдер ОТКАЗЫВАЕТ и рвёт
коннект, пока наше тело ещё пишется — байт ответа есть, статуса в руках нет, и чтение «байт = доставка»
оплачивает сметой каждый отказ (замер прежнего пака: 22 отказа из 25, один на $0.80).
**Два независимых замера на посадке (снятие `answered &&`), и они расходятся:** мой — **7 красных из 8**
(снят на нагруженной машине, рядом шла батарея); опровергателя на спокойной — **8 из 8**, а на сорока
процессах **39 FAIL / 1 PASS**. ⇒ пин краснеет по УДАЧЕ, это подтверждено обоими; частота зависит от
нагрузки, и поэтому «восемь красных подряд» **не является критерием приёмки**: сегодня, без единой правки,
восьмёрка выпадает примерно в 82 случаях из 100.
⛔ **А вот ПРИЧИНА не та, что я написал.** Я заказывал «сделать приход байта ответа НЕИЗБЕЖНЫМ до обрыва».
Зонд опровергателя (40 итераций, печать того, что реально получил клиент) дал **39 × обрыв с отказом
записи и 1 × ПОЛНЫЙ `401`**, и ни разу — «ни куска, ни статуса». То есть байт приходит практически всегда,
а мутант выживает в ДРУГОМ случае: клиент успевает вычитать `401` ЦЕЛИКОМ, `Do` возвращает не ошибку
записи, а ОТВЕТ, и `delivered()` не спрашивают вовсе — ошибка уходит статусной веткой. **Моё лекарство
било по половине, которая и так держится, и увеличивало вероятность ровно того исхода, в котором мутант
выживает.** (И «~6 МБ тела» тоже неверно: 11.0 МБ — Go не эскейпит кириллицу в JSON.)
**Задача — делай РОВНО так в части ЧТО, способ твой:**
1. **Тест утверждает СВОЮ ПОСЫЛКУ и печатает её:** что `Do` вернул ошибку ЗАПИСИ и что трасса видела
первый байт. **Сценарий не состоялся ⇒ тест КРАСНЫЙ**, а не зелёный «мимо предмета». Сегодня он зелен и
на полном `401`, то есть на другом предмете.
2. **Прямой табличный пин на сам `delivered()`** — он убивает посадку `answered &&` детерминированно, без
гонки вообще.
3. ⚠ У фикстуры `MaxAttempts: 3` — до трёх бросков кости на один `Complete`; **границу утверждай ПО
ПОПЫТКЕ**, а не по итогу вызова.
**Половина (а) — её первая редакция промта не заказывала вовсе.** Тот же ряд 379 несёт вторую половину:
две строки таблицы девяти в `backend/internal/pipeline/cutcall_test.go:402`=`time.Sleep(50 * time.Millisecond)`
разводятся ОКНОМ СТЕННЫХ ЧАСОВ, и по какую сторону границы заголовков сел `cancel()`, не утверждается
ничем — замер приёмки дал **2 красных из 8**. **Ряд закрывается ТОЛЬКО вместе с этой половиной.**
⛔ **Критерий приёмки берётся из самого ряда 379, он сильнее моего:** «каждый из двух пинов **ПЕЧАТАЕТ
величину, доказывающую пройденную границу, и ПАДАЕТ, когда сценарий не состоялся»**.
## 5. Где этот пак мягкий — четыре места, назвал я, веер и глубину выбираешь ты
Мандат самопроверки — **исполнением**; субагенты разрешены явно. Адверсариальный проход по СВОЕЙ ГОТОВОЙ
работе обязателен.
1. ⛔ **Пин, утверждающий только молчание, вакуумен ровно там, где сообщение ложно.** «Ошибка не вышла
наверх» — утверждение о молчании. Фикстура обязана быть такой, где новая причина ОБЯЗАНА прозвучать, и
тест утверждает её ИМЯ.
2. ⛔ **Фикстура обязана сделать отказ резервации НЕИЗБЕЖНЫМ на попытке ≥1 и НЕВОЗМОЖНЫМ на попытке 0** —
и окно считается по РЕАЛЬНОЙ оценке резерва, а не по «×2» (см. §1).
Денежный пин, флейковый на мутанте, измеряет пустой сценарий: у нас уже был пин, дававший 2 красных из
8, потому что деньги были ВЕРОЯТНЫ, а не неизбежны. Гоняй свой не один раз.
3. ⛔ **Посадка обязана бить в тот же слой, что стережёт пин.** За прошлый пак мутация трижды оказалась не
про то: недостижимое условие в фикстуре · не собирающаяся посадка (НЕизмеренная — хуже выжившей) · код
против гейта над данными.
4. ⛔ **Новая причина — факт ПРОВОДА, а не словаря.** Тест, проверяющий только, что константа добавлена в
`FlagReason`, не доказывает, что читатель её увидит. Скажи, чем предъявлена дорога от флага до
отчёта/экспорта.
⚠ И общее, стоившее трёх смен подряд: **зелёная батарея плюс полный мутационный каталог сходимостью НЕ
являются.** В последних трёх паках направленный второй читатель находил 6, 7 и 9 дефектов при полной зелени.
### ⭐ Адверсариальная стойка — способ смотреть, не правило пака
- ⛔ **Автор и ревьюер — разные роли, даже когда это один ты.** Перечтение своей работы рубежом не
считается: поднимай читателя, которому НАЗВАНО, где мягко.
- ⛔ **Спрашивай у аномалии, о ЧЁМ она — о предмете или о твоём приборе.**
- ⛔ **Верный результат при неверном методе не краснеет нигде.** «Сошлось» — не доказательство.
- ⛔ **Утверждение о молчании вакуумно** без фикстуры, где оно ОБЯЗАНО прозвучать.
- ⛔ **Заимствованное число проверяется не на существование, а на ТУ ЛИ КЛЕТКУ:** та же роль, та же модель,
та же стадия, тот же режим?
- ⭐ **Главный вопрос отчёта — не «что не получилось», а «что ты знаешь и не сказала».**
### ⭐ Находки ВНЕ заказа — отдельный заказ владельца
Слово владельца 11.09: **находить проблемы даже там, где мы не ждём.** Задавай себе в каждой фазе вопрос, у
которого нет заранее известного ответа: **что здесь стоит дороже, работает хуже или ведёт себя страннее,
чем должно бы, — и о чём никто не спрашивал?** Ради чего он заведён: удорожание из-за падающих запросов на
НАШИХ настройках никто не искал — просто кто-то посмотрел в леджер без гипотезы и увидел 25.4 % цены книги
в выброшенных попытках, при зелёных тестах и молчащих гейтах.
**Докладывай отдельной секцией: криты · мажоры · регрессии · баги · жёсткие точки улучшения** — каждое
аргументированно, с носителем (`file:line`, число, команда) и с ценой в деньгах, тексте или доверии
читателя. ⚠ Секция обязана быть непустой ИЛИ нести строку «искал вот так, не нашёл» с перечислением мест:
«находок нет» и «не смотрел» в отчёте выглядят одинаково.
## 6. Предметные оси ревью
Выбери 13 и назови какие. Мой приор: **деньги под гонкой** · **терминальное состояние прогона** (что
видит платформа и что видит человек) · **обратная совместимость словаря причин**.
## 7. Записка-план ДО работы
Перед первой правкой — записка: что меняешь, чем предъявишь, где ждёшь сопротивления. Комплектность против
заказа сверяй механически.
## 8. Заявление = команда
Каждое число и каждая категорика отчёта — **с командой, которой получены**. Дифф `^func Test` — **исполнением,
не памятью**. Приёмка пере-снимает.
⛔ **И три нормы кодового пака, каждая ловит свой класс:**
- **перед отчётом сверь КАЖДЫЙ клейм с результатом инструмента ЭТОЙ сессии** — не с памятью о прогоне;
- **последний абзац отчёта оказался планом или обещанием? Сделай его СЕЙЧАС** — обещание в отчёте не
переживает смены;
- **в длинной сессии — интервальная самоверификация субагентом против ЯВНЫХ критериев** (`D39.121`), а не
в конце: проверка, отложенная до сдачи, проверяет уставшую работу уставшим взглядом.
## 9. Эхо-протокол старта
**Порядок двух первых действий:** сперва впиши свой блок в `/tmp/textmachine-channel` (§12), потом отправь
эхо — эхо без блока некуда адресовать.
**Эхо** — ≤10 строк СВОИМИ словами: что понял · что считаешь опасным · что считаешь неверным.
Не пересказ: дословный пересказ подтверждает канал, но не понимание, и ошибку промта повторяет вместе с
ним.
**Адрес бери из `/tmp/textmachine-channel`** — блок с `role=оркестратор`, — а не из этого промта: имя сессии
не переживает рестарт окружения. Перед отправкой сверься с `ListAgents`: файл переживает смерть сессии, а
`ListAgents` — нет.
## 10. Что НЕ удалось — обязательная секция отчёта
И вторая её половина: **«где прибор слеп и я это знаю»** — что невидимо · почему не чинил · чем
закрывается. ⭐ Норма прошлой смены: **«в коде названо, в отчёте нет — значит для следующей смены НЕ
названо».**
## 11. Канал вопросов и право отказаться
Конфликт промта с кодом или доками — **пинг мне, не интерпретация**. Право сказать «этого делать не надо» с
аргументом у тебя есть и им пользовались: в двух случаях из трёх правы были сессии, а не я.
⛔ **Тесты и гейты под зелень не подгонять.** Правка, вызванная ЗАКАЗАННОЙ сменой поведения, — обслуживание,
и протухший тест держать не нужно; но она **ОБЪЯВЛЯЕТСЯ** в отчёте: что изменилось, какой тест это
описывал, куда уехала гарантия.
### ⭐ Старший коллега — Fable 5. **Разрешён ОДИН на сессию (слово владельца 11.09)**
У тебя есть право поднять **одного** агента на модели **`fable`** и держать его как СОВЕТЧИКА при сомнениях —
тем же способом, каким это делает оркестратор. Владелец разрешил ровно одного на сессию.
**Как с ним работать, и форма здесь важнее числа:**
- **Поднимаешь ОДНОГО и держишь его весь пак.** Ценность этого агента — в НАКОПЛЕННОМ контексте смены:
он помнит, что ты уже решил и почему. ⛔ **Второго не поднимай** — вопросы ДОСЫЛАЮТСЯ первому (`SendMessage`
по его id), а не адресуются свежему. Свежий агент вместо накопленного — потеря именно того, ради чего он
заведён.
- **Модель задавай ЯВНО** (`model: "fable"`), иначе она унаследуется и ты не будешь знать, что у тебя
работает.
- **О чём его спрашивать:** сомнение в решении · развилка, где оба пути выглядят законными · «не заказываю
ли я уже построенное» · «не противоречит ли это ратифицированному» · спорная формулировка в отчёте.
⚠ **Спрашивай С КОНТЕКСТОМ и со СВОЕЙ рекомендацией** — вопрос без твоего варианта ответа даёт совет ни
о чём.
- ⛔ **Он советчик, а не источник истины: его ответ проверяется деревом.** У оркестратора он за смену
ошибся дважды на фактах (называл платный прогон бесплатным, объявлял ряд новым предметом) — и оба раза
его ВЫВОД оставался верным. Проверяй посылки, принимай выводы по существу.
- **Что он дал оркестратору за эту смену** — для калибровки, чего от него ждать: поймал, что я собирался
завести новый ряд бэклога под предмет, у которого ряд уже был; развернул порядок паков доводом, который
я не назвал; и назвал условие, без которого два прогона стали бы несравнимы.
## 12. Прямой канал
Механизм — `CLAUDE.md` §«Связь между сессиями». Впиши свой блок в `/tmp/textmachine-channel` ПЕРВЫМ
действием. Нужной роли нет ⇒ канала нет, и это нормальный случай: НЕ опрашивай сессии подряд.
## 13. Критерий завершённости
У каждого пункта заказа — исход (сделано · не делаю с доводом · пинг) · круги СОШЛИСЬ (последний не дал
НОВЫХ находок; прежние закрыты таблицей «находка → что сделано → ЧЕМ ПРЕДЪЯВЛЕНО») · **таблица мутаций
полная, выжившие названы, и КАЖДАЯ новая запись помечена `battery`** (прошлый пак этого не сделал, и его
«21/21 RED» оказался разовым прогоном смены, а не гейтом проекта) · числа сняты ПОСЛЕ последней правки ·
всё живое в ДЕРЕВЕ, а не в письме · явное **«работа завершена, править не планирую»**. Без последнего пак
считается идущим.