# Промт: БЭКЕНД ДОФИКС-2 «ВХОДНАЯ ДВЕРЬ ШВА» — флажок становится флажком, точка фиксации отчёта, фикс-лист приёмки > Выдан оркестратором №19, 25.08.2026. Активный промт зоны бэкенда — один; этот заменяет > `BACKEND_SEAM_FIX_SESSION_PROMPT.md` (отработан, уезжает в архив при лендинге). > Параллельно законен только `platform/docs/archive/PLATFORM_P8_REVIEW_SESSION_PROMPT_2026-08-22.md (ОТРАБОТАН 27.08)` (read-only, пересечений нет). ## 1. Какая проблема и что решит твой результат Продукт — издательский перевод больших книг мультиагентным пайплайном. Центральная ценность — **банк памяти**: сквозной глоссарий имён и терминов, держащий консистентность на всю книгу. Две предыдущие сессии построили **входную дверь**: `tmctl bank-apply` принимает решения владельца данными, а не правкой YAML руками. Дофикс закрыл блокер формата и восемь мажоров. Приёмка прошла и снова не приняла — но главная её находка не в двери. **Продуктовая модель, ратифицирована D39.144 прямым словом владельца:** стоп банка — это **флажок**. Опущен — прогон не останавливается, неподписанные строки едут редактору с пометкой. Поднят — полный останов, пользователь говорит «ОК», прогон продолжается. Пер-термной подписи в продукте **нет**: владелец подписывает банк целиком либо правит один термин, и правка — отдельная способность, доступная всегда, в том числе после перевода. **Движок этой модели не реализует.** Стоп гаснет, только когда КАЖДАЯ предложенная поверхность промотирована или отклонена. Продукт ведёт себя правильно исключительно потому, что **платформа снимает флаг при возобновлении** своей персистентной колонкой. ⚠ **Это НЕ новое открытие, и провенанс записан — прочти его прежде, чем проектировать.** Формула «стоп гаснет, когда каждый терм промотирован или отклонён» родилась В ДВИЖКЕ 19.07 как лечение ливлока (отклонённый терм переспрашивался каждый прогон), то есть как условие выхода из паузы ОПЕРАТОРА CLI. Контрактная линия три недели спустя переписала её дословно и вывела продуктовое правило, которого никто не заказывал. D39.144 гейт сняла, но адресовала только КОНТРАКТ — движковая половина осталась без явной отмены и сегодня ОБОЙДЕНА, а не устранена. Носитель — строка бэклога **191(б)**, эррата — в шапке журнала решений (D39.153 п.7б). ⚠ **Слово владельца 26.08, ПЯТЫЙ и решающий носитель:** «стоп на банке памяти — это просто флажок. Юзер поднял флаг — полный останов до его ОК; опустил — перевод не останавливается. Всё». Канонизации в жесте «ОК» НЕТ: он значит «продолжай», а не «сделай предложенное каноном». ⚠⚠ **И РАЗБЕРИСЬ С ОДНИМ ПРОТИВОРЕЧИЕМ ДО КОДА, иначе ты встанешь на нём по §11.** Строка бэклога 191 говорит: «ОСТАТКА-РАТИФИКАЦИИ НЕТ… гейт проверяет полноту ФАЙЛА решений, а не подписей, и ОДИН файл со всем банком его снимает — конфликта с моделью владельца нет». Это **не ошибка строки и не опровержение промта** — она отвечала на ДРУГОЙ вопрос. Сверялось составное поведение: платформа снимает флаг, движок уходит по авто-ветке, зависания нет. Верно. Но её же живой остаток записан там дословно — «снять обход в платформе», — и **снятие обхода возвращает границу паузы в несущие**. Её пункт (б) заказывал «проверить/ослабить границу самой паузы»; исполнено было только «проверить». Ты делаешь отложенное «ослабить», и делаешь его в движке, потому что по п.1 закона поведение движка принадлежит движку, а не колонке чужой базы. Эррата к строкам 191 и 203(з) — на оркестраторе, при лендинге; тебе это знать, чтобы не встать. ⚠ **Модель ПЕРЕ-ОПРЕДЕЛЕНА пятью носителями — не выводи её заново, ни из кода, ни из разговора.** Нота D39.144 · продуктовый реестр `docs/product-requirements.md`, строка **ПТ-24** («ОДИН ОК всему банку, не пер-термная масс-подпись») · экран банка во фронте, где прямо написано, что пер-термного состояния подписи на экране нет СОЗНАТЕЛЬНО и колонка «подписано» спорила бы с продуктом · надгробие снесённой ручки на платформе. Две сессии и два консилиума выводили её из кода и оба раза ошиблись. ⚠ **И гардрейл оттуда же, ПТ-24, дословно:** пост-ридинговые точечные правки с пере-генерацией по вхождениям — «механика **после полигона**». Значит **не проектируй** ничего про то, как правки работают ПОСЛЕ прочтения книги: ни глубины, ни отдельного цикла, ни новых полей под это. Смежный механизм точечной ре-редактуры уже существует и срабатывает на сдвиг банка — трогать его не надо. **Твой результат: флажок работает как флажок в самом движке**, без участия чужой зоны, и вместе с этим закрывается точка фиксации отчёта, из-за отсутствия которой четыре раунда ревью подряд находили новую ветку одной дыры. ⚠ **Дерево двух предыдущих паков НЕ закоммичено и лежит в рабочем каталоге.** Ты продолжаешь его. ## 2. Зона записи и git Пишешь ТОЛЬКО в `backend/`. Читать можешь всё; `platform/`, `frontend/`, `eval/`, `docs/` — чужие зоны, правки в них запрещены. **Единственное исключение:** пинги и итог сессии — в `docs/PROGRESS.md`, секция «Бэкенд». **Ты не коммитишь** — дерево готовишь и передаёшь на лендинг оркестратору (канон git-норм — `CLAUDE.md`). `git add -A`, `git add .`, `git commit -a` запрещены в любом случае. В дереве живёт незакоммиченная работа параллельных сессий — не трогай её и не «прибирай». ## 3. Карта чтения (пять позиций) 1. **`docs/architecture/05-decisions-log.md`, нота D39.144** целиком, **и эррата к ней в шапке журнала** (D39.153 п.7б). Это ратифицированная модель и история вопроса. ⚠ Читать ДО кода: две сессии подряд восстанавливали этот провенанс из кода, потратив на это раунды. 2. **`docs/PROGRESS.md`, строка БЭКЛОГА 191** ⚠ ID строки ТАБЛИЦЫ, брать грепом `^| 191 |`. Носитель темы, открытый ровно под твою работу. 3. **`docs/architecture/17-seam-inbound-law.md`** — закон входной двери. П.1 («движок владеет своим поведением») — то, что сегодня нарушено. 4. **`backend/docs/SEAM_PACK_FINDINGS.md`** — доказательная база двух паков. Вход — §26. ⚠ Секции 0–13 — улика первой приёмки, не трогай. 5. **`docs/architecture/12-go-style-notes.md`** — норматив общности. **Код первичен.** Все `file:line` ниже — отправные точки приёмки и сплошного чтения; проверяй сам. ## 4. Состав работы Тринадцать пунктов (`grep -cE '^### 4\.'` → 13). §4.1–§4.4 — семантика флажка, §4.5–§4.8 — точка фиксации и полоса, §4.9–§4.12 — фикс-лист. Комплектность сверяется механически (§7). ### 4.1 «Остановиться один раз» становится родным для движка — ДЕЛАЙ РОВНО ТАК инвариант, РЕШИ САМ форму Движок заводит персистентную **память «предъявленного стопом»** — поверхности, за которые флаг уже заплатил остановкой. Стоп срабатывает, только если в текущем наборе есть поверхность **вне запомненного**. Предикат — «есть ли новое», **не равенство наборов**: равенство остановило бы снова на подмножестве уже виденного после первой же правки. ⚠⚠ **Домен предиката задан жёстко, и промахнуться тут дорого.** «Набор» — это **содержимое КАРТЫ этого стопа, обе секции, ПОСЛЕ всех потолков**; память — кластеры этого содержимого. Прочтение «набор = ранжированный пул ДО потолка» превращает флажок в ВЕЧНЫЙ стоп: постоянно невидимые за потолком поверхности (§4.4) делают «есть новое» истинным на каждом прогоне навсегда. **Проектные решения, каждое с доводом — они выведены сплошным чтением, не догадкой:** - **Ключ — нормализованная поверхность** (`text.NormalizeSourceKey`, тот же, что у отказов и двери), **а не пара (поверхность, перевод)**: предложенный перевод двигается от подписи СОСЕДНИХ термов (`terminologist.go`, ранжирование вариантов через `approvedNeighbours`), и память по паре дала бы вечные стопы. - ⚠ **Эмиссия работает КЛАСТЕРАМИ, а не поверхностями.** Представитель кластера — первый в ранжированном пуле, и при росте книги он может смениться на алиас той же сущности. Память по одной поверхности этого не переживёт — лишний стоп на том же термине. Клади в память кластер (`Src` и `Aliases`, они уже посчитаны) и спрашивай про пересечение, а не про одну поверхность. ⚠ Остаток у кластерной памяти ДВУСТОРОННИЙ, и записать надо обе стороны, а не только удобную. **Мягкая:** сущность вернётся под написанием, которого в памяти нет — один лишний стоп, потерь нет. **Вредная:** подлинно НОВАЯ сущность, делящая поверхность-алиас с запомненным кластером (родной пример репозитория — одно прозвище на двух разных персонажей), пересечением пометится виденной и **не будет предъявлена молча**. Смягчено тем, что терм всё равно поедет редактору авто-строкой, но направление противоположное. Обе — ПРИНЯТЫЕ ОСТАТКИ, обе в отчёт. - **Где живёт.** Состояние КНИГИ, в store. ⚠ Прецеденты, которые я называл в прошлой редакции промта, **прецедентами не являются**, и я это снимаю: список отказов — ФАЙЛ рядом с книгой, а `unitOnceKey` живёт в `events_outbox`, который сам себя называет БУФЕРОМ, а не архивом, и чистится. Форму долговременного состояния книги выбираешь ты и аргументируешь. - ⚠ **Цена миграции названа заранее.** Новая ТАБЛИЦА (`CREATE TABLE IF NOT EXISTS`) — дешёвая форма; `ALTER` существующей упирается в известный долг «шаги v8+ не идемпотентны» (строка 49а). Любая миграция двигает голову схемы, а `OpenReadOnly` отказывает при несовпадении — то есть все непере-мигрированные книги встают, и это ровно дедлок, который закрывали глаголом `tmctl migrate`. Порядок деплоя это покрывает, но в отчёте назови явно. - Книга без базы: отсутствие базы уже трактуется как «ничего не решено» — память унаследует «ничего не предъявлено», и это правильно. - **Политика роста: НЕ обрезать.** Память ⊆ намайненного словаря книги — тот же закон роста, что у списка отказов, который никто не обрезает. Без этой строки исполнитель изобретёт TTL и тем вернёт повторные стопы через заднюю дверь. ### 4.2 Порядок и момент записи — ДЕЛАЙ РОВНО ТАК **Сначала карта (атомарно), потом память.** Падение между — лишний доброкачественный стоп на следующем прогоне; обратный порядок — поверхности помечены предъявленными, а карты нет: молчаливая потеря навсегда. ⚠ **Это зависит от §4.10: карта сегодня пишется НЕ атомарно.** Порядок работ не свободен — атомарность карты идёт первой. ⚠ Между картой (файл) и памятью (SQLite) общей транзакции нет. Реши явно и назови: валит ли отказ записи памяти уже состоявшийся стоп, или стоп остаётся, а память допишется на следующем. **Память пишется в момент СТОПА, не в момент «ОК».** Тогда возобновление остаётся вызовом без полезной нагрузки, и нового глагола не нужно. ⚠ **Никакого «подтверди карту» в дверь не заводить:** согласия, которое надо якорить, в модели владельца не существует — это была ошибка приёмки, снята. ⚠ Ветка «только черновая волна»: карта пишется, стопа нет — памяти по букве тоже нет. Доброкачественно, но назови. ### 4.3 Авто-провод обязан работать при ПОДНЯТОМ флаге — ДЕЛАЙ РОВНО ТАК Сегодня авто-провод закрыт условием «флаг НЕ поднят»: в этой ветке пишется авто-банк, пере-сидируется банк, обновляется экспорт. С поднятым флагом — ничего этого нет. Сегодня не кусает только потому, что платформа флаг снимает. **Ты этот сброс отменяешь** — значит прогон с поднятым флагом, который НЕ остановился (нового нет), обязан идти дальше **полноценно**: записать авто-банк, пере-сидировать, обновить экспорт. Иначе «неподписанные строки едут редактору» отваливается ровно в том режиме, ради которого всё делается. Перестрой ветвление так, чтобы решение «останавливаться ли» и решение «проводить ли неподписанное» были РАЗНЫМИ решениями. Сегодня это одно `if`. ⚠ И назови в записке ВТОРУЮ половину, которую этот пункт словами не покрывает: пишет ли авто-банк прогон, который ОСТАНОВИЛСЯ. Через чекпоинты терминолога сходится в обе стороны, поэтому решение должно быть НАЗВАННЫМ, а не случайным. ### 4.4 Хвост за потолком — НЕ ЛЕЧИТЬ, а честно назвать — ДЕЛАЙ РОВНО ТАК ⚠ **Прежняя редакция этого промта заказывала фильтр карты как лекарство от голода. Заказ СНЯТ — он стоял на моей ошибке, и вот факт, который её опровергает.** Потолок эмиссии (200) применяется **ДО** фильтров пригодности, сида и отказов, а детектор кандидатов сида и отказов не видит вовсе. Репозиторий это про себя знает: «*applied BEFORE the emission filters, so seed and declined terms do not free their slots*». Замерено сплошным читателем на синтетическом корпусе: 2110 ранжированных, 442 годных во всём алфавите, 117 внутри окна потолка, **325 невидимы постоянно**; отклонение всех 117 не подняло ни одного из 325. Значит: **хвост за потолком недостижим при любом решении владельца, и это верно СЕГОДНЯ**, а не станет верным от твоей правки. Цитата про «пере-предлагается once these are signed or declined» относится к ОБРАТНОЙ секции, где фильтры действительно стоят до потолка. **Заказ:** ничего не лечить. Назвать это в отчёте и **пингом** как отдельный предмет: «майнерский хвост за потолком структурно недостижим; двигать потолок — трогать фрозен-паритет, отдельное решение». ⚠ **И не заводить фильтр карты «чтобы освободить хвост»** — он не освободит, а сломает три вещи: карта пишется на КАЖДОМ прогоне до проверки флага; отказы фильтруются на общем списке, из которого кормятся и карта, и авто-банк, и кандидаты терминолога (ветки после него нет); а смена состава кандидатов промахивается мимо чекпоинтов терминолога и **пере-оплачивает платную роль**. ### 4.5 Единая точка фиксации отчёта — ДЕЛАЙ РОВНО ТАК инвариант, РЕШИ САМ форму **Класс, из-за которого четыре раунда ревью находили новую ветку одной дыры.** Отчёт рождается до того, как исход вызова существует, и каждая ветка выхода правит свой кусок руками. Ветка отказа откатывает три поля предупреждения; ветка прерывания откатывает режим, флаг и список — и **не откатывает `signature`**, поэтому вызов, не записавший ни байта, печатает «не решено: 0». А ветка записи не правилась никогда: **при отказе записи наружу уходит отчёт успешной формы**. Приёмка это воспроизвела: `EXIT=1`, отчёт `mode=apply changed=true`, оба решения `applied`, на диске дельта записана, реджекты пусты. **Заказ — два движения, нужны оба:** **(а) Разделить отчёт по природе полей.** *Вердикт о запросе* (что принято, что отвергнуто и почему, индексы, `replaced`, `preexisting`, `depth`, `files`) — факт о решениях против состояния ДО; его на диске нет и не будет. *Исход* (режим, `changed`, предупреждения о пере-рендере, `signature`) — факт о мире ПОСЛЕ. **(б) Один конструктор на одном выходе.** Эффектная фаза возвращает ЯВНОЕ значение исхода — проекция · отказ · no-op · прерывание · записано · запись не удалась — и единственная функция заполняет исходные поля свитчем по нему. Для «записано» пост-состояние — **перечитанные** файлы. Новая ветка тогда не сможет забыть поле: ей негде забывать. Пин — тест, перечисляющий исходы и проверяющий происхождение каждого поля. ⚠ Сильная форма «глагол вообще не сочиняет отчёт» НЕВЕРНА для вердиктной половины: причины отказов и история `replaced` на диске не лежат, и дверь потеряла бы собственный продукт. ### 4.6 `signature.undecided` пере-документируется, а не удаляется — ДЕЛАЙ РОВНО ТАК Сегодня контракт поля говорит: «*Undecided > 0 значит, что стоп НЕ погаснет, и это факт, не догадка*». После §4.1 это **ложь**: стоп теперь зависит от памяти, а не от полноты решений. При этом канон понизил счётчик до информационного, а не до несуществующего — **не удаляй**. Пере-документируй честно: что поле считает и чего оно больше не обещает. ⚠ И учти, что дверь открывает базу только на чтение — если ответ теперь требует памяти, реши, читает ли она её и на каком основании, или поле перестаёт отвечать на вопрос о стопе вовсе. ### 4.7 Полоса отказов получает два яруса — ДЕЛАЙ РОВНО ТАК - **Пол полосы** переформулируется из «ничего не произошло» в **«нет работы, которую надо откатывать; повтор безопасен»**. «Повтор безопасен» = «не навредит», НЕ «поможет». - **Каждый класс поверх пола объявляет свой, более сильный инвариант.** Конфиг, источник, лок и отвергнутые решения сохраняют «ничего не произошло» как клаузу КЛАССА. - **Гардрейл:** разрушительное действие ключуется **только на классе**, никогда на принадлежности полосе. Сегодня истинно де-факто (проверено приёмкой) — становится буквой без миграции. - **Один новый класс на «запись не завершена»** — и для «не легло ничего», и для «легла половина»: потребитель ветвится одинаково, различие несёт пофайловый отчёт. Не сворачивать в класс конфига. - **Класс отвергнутых решений однофóрмен:** отказ по капу тоже печатает отчёт. Сегодня он печатает только строку в stderr, и контракт разошёлся между двумя своими носителями — свести к одному. ### 4.8 Подготовка обоих файлов, потом два переименования — ДЕЛАЙ РОВНО ТАК Подготовить ОБА временных файла целиком (запись + синк), и только потом два переименования подряд. Тогда весь класс отказов среды ловится **до первого сдвинутого байта**, и репро §4.5 падает, ничего не записав. ⚠ **Не заявляй шире сделанного:** это атомарность **против ошибок среды**, не против смерти процесса — окно между переименованиями остаётся, и против SIGKILL атомарность пары требует журнала, механизма, которого дверь не заслужила. Остаток покрыт замеренной сходимостью (приёмка померила: повтор после половинной записи сходится и ПРАВИЛЬНО различает половины), а убитый процесс отчёта не печатает. ⚠ Отдельно: запись синкает файл перед переименованием, но не синкает КАТАЛОГ после. Файлы решений — слова пользователя: либо синк каталога, либо явная оговорка про долговечность. ### 4.9 `Clean` в резолве и конвенционные пути вместо ключей — ДЕЛАЙ РОВНО ТАК **(а)** Попарная проверка путей сравнивает СТРОКИ, а резолв возвращает абсолютный путь без `filepath.Clean`. Приёмка обошла гард и воспроизвела **исходный блокер целиком**: два ключа на `/shared.yaml` и `/./shared.yaml` → `exit 0`, оба решения `applied`, на диске только reject-лист, одобрение исчезло молча, следующий вызов включая `--dry-run` падает `exit 10`. **(б) Убрать оба ключа конфига: пути только конвенцией.** Тогда путь есть функция от каталога и `book_id`, и межкнижная гонка недостижима по построению. Цена замерена: из 42 `book.yaml` ни один не объявляет читаемый путь, отличный от конвенции. Миграция громкая и дешёвая: строгий декодер уронит книгу с объявленным ключом, лечение — удалить две строки. ### 4.10 Карта подписи: атомарная запись и конверт — ДЕЛАЙ РОВНО ТАК Карта пишется голым `os.WriteFile` в обход атомарной записи — единственный артефакт стопа без неё, при том что её читает другая сторона, а операционное правило платформы прямо говорит «на живом прогоне брать нельзя». С §4.2 это перестаёт быть гигиеной. И у карты **нет никакой идентичности** — ни версии, ни хеша: единственный артефакт шва вне п.3 закона. Заведи конверт с версией и идентификатором, выведенным из СОДЕРЖИМОГО. ⚠ **Карту не превращать в накопитель:** комментарий в коде предупреждает, что слияние старой карты в новую изменило бы содержимое банка, сдвинуло версию памяти и пере-оценило редакторскую волну. ### 4.11 Слепые пятна гейта — ДЕЛАЙ РОВНО ТАК Каждое — место, где приёмка посадила мутацию и **батарея осталась зелёной**; все пере-проверены во всех четырёх кандидатных пакетах пина. | что не сторожится | мутация, которую пин обязан поймать | |---|---| | нормализация, суженная до одного триггера вместо класса | `normText`: `TrimSpace` → `TrimLeft(s, "\n")` | | субъект проблемы схлопывается до класса | `subjectOf` возвращает только `class` | | субъект теряет окно глав | `subjectOf` без `since`/`until` | | абсолютность путей в отчёте `bank-apply` (в статусе та же гарантия запинена) | убрать приведение к абсолютному | | границы обоих капов | `>` → `>=` | | граница проверки перевёрнутого окна | `>` → `>=`, отвергает законное одноглавное окно | ⚠ **И самое неприятное: сегодня семантическое изменение §4.1 не краснит НИ ОДИН тест бэкенда.** Это само по себе находка. Твой пак обязан завести пин, который краснеет от возврата к старой семантике, — иначе следующая сессия вернёт её молча. ### 4.12 Наследие отменённой модели — снести СПИСКОМ — ДЕЛАЙ РОВНО ТАК ⚠ Список, а не память сессии: починишь гейт и оставишь сообщение — следующий читатель движка выучит отменённую модель из его же слов. - **Гейт «стоп гаснет от полного набора решений»** — снимается §4.1. - **Операторское сообщение стопа**, дословно учащее «промотируй ИЛИ отклони КАЖДЫЙ терм, потом возобнови — стоп погаснет, когда решён каждый». Переписать под модель флажка. - **Комментарии `mining.go` в шапке функции стопа и у `unsignedEngineSurfaces`; `waverun.go`; рендер CLI** — сплошной читатель называет их поимённо, пройди по ним. - ⚠ **Новое состояние, которого раньше не было: «дельта непустая, нового нет».** Сегодня флаг меняет лишь уровень лога на пустой дельте; теперь оператор с поднятым флагом может не получить ничего, и молчание будет неотличимо от «майнинг не сконфигурирован». Нужна явная строка. - **Цепной тест** `TestTheStopClearsWhenTheOwnerDecidesTheWholeSignatureMap` — ⚠ **не удалять до §4.1** (подгонка зелени под доктрину). Он пройдёт и после §4.1, но по другой причине — потому что память погасит стоп, а не потому, что подписи что-то значат. **Перепиши его под новую семантику и назови замену явно.** - **ПИНГИ ЧУЖИМ ЗОНАМ, не правки:** у платформы умирает сброс флага, особый случай в спавне и персистентная колонка · во фронте и в полигоне есть потребители карты подписи, и смена её жанра их задела бы. Выпиши пингом в свою секцию журнала. ⚠⚠ **И назови в пинге ПОРЯДОК, потому что опасная половина пишется легче.** Сброс флага платформой может умереть ТОЛЬКО ПОСЛЕ того, как §4.3 залендится в движке. Обратный порядок — и в день лендинга каждая книга с поднятым флажком перестаёт писать авто-банк и пере-сидироваться ровно в том режиме, ради которого флажок существует. Порядок деплоя «движок первым» это обеспечивает, но зависимость обязана быть НАПИСАНА, а не выведена. - **Миграционная нота:** после апгрейда пустая память даёт один доброкачественный стоп. Назвать. ### 4.13 Деньги: платных вызовов НОЛЬ — ДЕЛАЙ РОВНО ТАК Санкции нет и не запрашивается. Всё проверяется юнит-тестами, фейковым провайдером и пробой на копии книги стенда. Упёрся в место, где без денег не доказать, — **пинг с названной суммой, не трата**. Рантайм-вердикт по платному пути остаётся PLAUSIBLE. ⚠ **Гипотеза о побочном выигрыше — НЕ выгода, пока не предъявлен механизм.** Есть подозрение, что подписание сегодня промахивается мимо чекпоинтов терминолога и пере-оплачивает платную роль (ключ чекпоинта — хеш отрендеренных сообщений, а состав кандидатов меняется от дельты и отказов). Память предъявленного эти входы **не трогает**, поэтому «память чинит перерасход» механизмом не подтверждено и в отчёт как выигрыш идти не должно. Если увидишь механизм — покажи `file:line`; если перерасход существует сам по себе — это отдельный предмет и **пинг**, не твой заказ. ## 5. Мандат самопроверки ИСПОЛНЕНИЕМ Не «перечитал» — **исполнил**. Минимум: - **Батарея целиком, ИЗ КАТАЛОГА `backend/`**, после каждой содержательной правки: `go build ./...`, `go vet ./...`, `go test ./... -count=1`, `make lint`. ⚠ `go.mod` в корне репозитория НЕТ. ⚠ **И `make battery-stand` минимум один раз в конце.** Ожидаемая картина: всё зелено, кроме парити на отсутствующем jieba-контрасте (вне git по построению, строка 123 — не твой дефект). Красный тест не «подгоняется»: править или удалять тест, голден или гейт ради зелени — **НЕДОПУСТИМО**. ⚠ В `internal/store` два пред-существующих `-race`-флейка, оба меряют ВРЕМЯ; приёмка доказала тремя путями, что они не от паков. Красное именно в них — шум. - **Каталог мутаций расширяется:** `cmd/tmmutate` + `mutations.json` несут 36 записей; каждая правка §4 обязана получить свою. ⚠ Инструмент честен — приёмка подсунула ему несобирающуюся посадку, он назвал её `NOTHING`, а не «поймано»; не сломай это свойство. ⚠ Посадка называет ПАКЕТ, в котором ищется пин, — не тот, где лежит правка. - **Живая проба на копии книги стенда**; в `books/` не писать вовсе — чужой git-репозиторий. - **Субагенты РАЗРЕШЕНЫ явно** — на ревью своего кода, на поиск дефектов вне карты, на опровержение твоих же выводов. ⚠ Соразмеряй параллельность с ресурсами: агенты, одновременно гоняющие полную батарею, топят друг друга, а упавший от нагрузки выглядит как отсутствие находок. - **Артефакт-находки обязателен:** дописывай `backend/docs/SEAM_PACK_FINDINGS.md` новой секцией, не переписывая старые — они улика двух приёмок. ### 5.1 Обязательное для кодового пака - **Последний абзац отчёта — план или обещание? Сделай его СЕЙЧАС.** - **Дифф `^func Test` — исполнением.** - **Интервальная самоверификация субагентом** против явных критериев в середине работы. - **Перед отчётом сверь каждый клейм с результатом инструмента ЭТОЙ сессии.** - ⚠ **Закрытие проверяется ОТДЕЛЬНО от починки и не тем, кто чинил.** Предыдущая сессия вывела это правило сама: её собственная проверка закрытия нашла три незакрытых пункта из 28, а приёмка — ещё два частично закрытых, один из которых вернул исходный дефект. - ⚠ **И правило, которое стоило этому проекту трёх раундов подряд: прежде чем проектировать — прочти ратифицированное.** Две сессии и два консилиума восстанавливали из кода то, что лежало записанным в журнале решений. Твоя карта чтения начинается с ноты не случайно. ## 6. Оси ревью (три) 1. **Семантика флажка** — делает ли движок то, что описано в D39.144, **без участия платформы**. Проверяется прогоном движка напрямую, а не через её код. 2. **Единая точка фиксации** — для каждой ветки выхода: откуда взято каждое поле отчёта, и может ли новая ветка забыть поле. Если может — конструктор не сделан. 3. **Слепота гейта** — какой мутацией сторожится каждая правка и краснеет ли пин НА САМОМ ДЕЛЕ. Ты вправе ЗАМЕНИТЬ любую ось своей с аргументом. ## 7. Записка-план До кода — короткая записка `backend/docs/SEAM_FIX2_PLAN.md`: что делаешь, в каком порядке, какие развилки видишь. Комплектность против §4 сверяется МЕХАНИЧЕСКИ: `grep -cE '^### 4\.'` обязан дать **13**, а не глазами. ⚠ **Пак большой.** Если считаешь, что семантика флажка (§4.1–§4.4) и точка фиксации (§4.5–§4.8) должны ехать разными паками — скажи это аргументом, это законный ответ по §11. Мой довод за один пак: обрамление отчёта умирает в обоих, и разделение повторило бы ровно ту патологию «починить одну ветку», из-за которой четыре раунда находили новую. ## 8. Заявление = команда Каждое число и каждая категорика в отчёте — **с командой, которой получены, прямо рядом**. Приёмка пере-раняет выборочно. ⚠ **ИСПОЛНЕНО ДО ТЕБЯ — только перепроверь, не делай заново.** Довод к правке общего гейта в `backend/Makefile` называл **558 с** на `internal/pipeline` под `-race` со словом «idle». Приёмка померила на свободном хосте **92.8 с** в батарее и **97.6 с** изолированно; автор прежнего числа назвал условия — его замер шёл под тремя параллельными сессиями, то есть неверно было слово «idle», а не факт. Сессия предыдущего пака переписала довод сама: теперь это ДИАПАЗОН с условиями обоих концов, без «idle», без привязки к машине и без процента от дефолта. Твоё дело — убедиться, что так и осталось, и не «улучшить» обратно в одно число. Урок, ради которого это здесь: **число без условий замера — не число.** Оба замера были настоящими и расходились в шесть раз от одной только нагрузки. ## 9. Эхо-протокол старта ДО работы — не больше десяти строк: скоуп как ты его понял · инварианты, которые не тронешь · что делать НЕ будешь. ## 10. Что НЕ удалось Обязательная секция отчёта. «Не проверено» ≠ «работает». Вердикт о рантайме без живого прогона — максимум PLAUSIBLE. ## 11. Канал вопросов Конфликт промта с кодом или доками — **пинг через владельца, не интерпретация**. Ты вправе сказать «этого делать не надо» с аргументом. §4.2, §4.4, §4.6 и §7 прямо приглашают такой ответ. --- ## Приложение: что приёмка и сплошное чтение ПОДТВЕРДИЛИ — не чини заново - **Блокер формата закрыт на уровне инварианта:** round-trip держится на всех текстовых полях обоих документов на девяти неудобных значениях. - **Каталог мутаций воспроизведён** оркестратором: 36 посадок, 0 неожиданных исходов, дерево чистое. - **Оба капа работают**; **цепь** доходит до редакторской волны; **батарея зелёная**, линтер 0 issues; стенд-батарея красна ровно на парити. - **Рукописные поля владельца переживают решение** — род, тип, склонение с формами, алиасы, голоса. - **Идемпотентность работает в отказе:** после половинной записи повтор сходится и правильно различает половины. - **Мины со снапшотом с этой стороны НЕТ:** ни карта, ни таблицы, ни новая таблица store в версию памяти не входят — предупреждение в коде касается накопления в авто-банке. - **Ключ по поверхности, а не по паре** — подтверждён сплошным чтением с доводом сильнее исходного. - **Отказы и память не дублируются:** отказ гасит кластер и работает ещё на входе в банк и в обратной секции; память — только про стоп. Слить их нельзя: отклонённый терм поедет редактору. ## Приложение: пинги, которые дофикс-2 НЕ исполняет - **Майнерский хвост за потолком структурно недостижим** (§4.4) — отдельный предмет. - **Банк-экспорт на стопе ПУСТ** (замерено сплошным читателем: `total=0`), при том что комментарий рядом называет стоп «границей, которую читает экран подписи», а машинную таблицу стопа не читает никто. Предмет платформенно-продуктовый, не твой. - **exit 2 при нехватке памяти читается платформой как успешный прогон с флагами** — общедвижковый класс. - **`readEngine` платформы выбрасывает stdout при любом ненулевом коде, кроме 2** — требование к платформенному паку: отчёт при отказе И ЕСТЬ продукт вызова. - **Развилка про канал ключей `redrive`** · **`rubyToCandidates` мёртв в проде** · **гейт диспетча слеп к `case` с константой** · **схема сида без `omitempty`** — пинги прошлых паков, остаются пингами. - **Проекция берёт тот же лок, что и применение** — проектируется платформенным паком. --- ## Записка от бэкенд-сессии двух предыдущих паков (написана до компакта её контекста, 27.08) Я — сессия, которая строила дверь и оба дофикса. Дерево НЕ закоммичено, ты его продолжаешь. Здесь не инструкции — реонбординг: где что лежит, что я подтверждаю своим контекстом, на чём обжигался. Свежий взгляд с нуля ценнее моих планов, поэтому планов нет. ⚠ Прежняя моя записка (онбординг в формат и историю) живёт в хвосте `BACKEND_SEAM_FIX_SESSION_PROMPT.md` и уедет в архив — несущее повторено здесь. ### Реонбординг в код — обе точки входа разобраны на ИМЕНОВАННЫЕ фазы, и это твой рычаг - `internal/membank/decisions.go` — чистая семантика. `ApplyDecisions` = `resolveAll` → `refuseSeedConflicts` → `foldAccepted` → `refuseInertDeclines` → `refuseNewFaults`; мутирует РОВНО одна фаза (`foldAccepted`), остальные судят. Прочитать пять имён = прочитать контракт двери. - `internal/pipeline/bankdecisions.go` — обвязка: `openDecisionRequest` → лок → `readBookState` → `ApplyDecisions` → `decisionReport` → `changedFiles` → `writeDecisionFiles`. **§4.5 садится ровно сюда**: отчёт рождается в `decisionReport` ДО исхода, и каждая ветка выхода после неё правит поля руками. Подтверждаю оба репро приёмки из своего контекста — это мои недочинки: stopped-ветка чинит `mode`/`changed`/`accepted` и НЕ трогает `signature` (он считается в `signatureState`, поле `unreadable` там уже есть — пригодится в §4.6); ветка отказа записи не правилась никогда. - §4.1–§4.3 живут в `internal/pipeline/mining.go`: `runBankMiningStop` — стоп и авто-провод сегодня действительно одно `if !r.VerifyBank`; рядом `unsignedEngineSurfaces` (фильтр «подписанного»), `ownerHandled`, `rejectSurfaces`, `signatureMapPath` (есть и пакетный помощник, не только метод). Store: `internal/store/store.go`, схема v15, `tmctl migrate` есть, `LockProject`/`OpenReadOnly` готовы. - Карта «предмет → пин»: `membank/decisions_test.go` (семантика) · `decisions_fuzz_test.go` (round-trip, три фаззера) · `pipeline/bankdecisions_test.go` (обвязка, классы, стоп-путь) · `bankchain_test.go` (цепь; НАМЕРЕННО писан под старую семантику — §4.12 велит переписать; сохрани его премисс-проверки, фикстура даёт ровно «2 терма = 1 approve + 1 decline») · `statusartifacts_test.go` · `config/decisionpaths_test.go` · `cmd/tmctl/keysfile_test.go`. ### Инструмент, без которого не сдавай `cmd/tmmutate`: `-root` ОБЯЗАТЕЛЕН и это должна быть КОПИЯ (`cp -a backend /scratch/x && cd /scratch/x && go run ./cmd/tmmutate -root .`). 36 записей, три вердикта (RED / NOTHING / survives), зелёная база пакета ДО первой посадки. ⚠ Якоря записей — точные строки кода: твой рефакторинг их сдвинет, инструмент скажет громко — пере-указывай сразу, это нормально. Со мной это однажды случилось МОЛЧА (старая версия считала провал сборки красным, и две записи целую итерацию числились пойманными тестами, которые не могли отработать) — теперь `NOTHING` роняет гейт; промт прав, что это свойство ломать нельзя. ### Ловушки, каждая стоила мне прогона 1. **Живая проба на стенде**: копия ВНЕ дерева; мёртвые абсолютные пути в ТРЁХ файлах (`book.yaml`, `pipeline.yaml` и `pairs/zh-ru.yaml` — в последнем `prompts_root`, про него забываешь); база схемы v11 → `tmctl migrate` на копии; банк-стоп на этой машине НЕ воспроизводится (нет jieba-контраста) — для стопа и цепи бери харнесс `miningstop_join_test.go`, он $0 и уже гоняет всё это. ⚠ К §4.9(б): стендовые `book.yaml` объявляют оба ключа — в КОПИИ удали эти две строки сам; на самом стенде это не твоя зона (пинг оркестратору отправлен, он в курсе). 2. **Батарея**: полная на ненагруженной машине зелёная ЦЕЛИКОМ; два store-флейка изолированно через `-run` падают детерминированно на обеих сторонах A/B (сжатый `opTimeout`) — не отнеси на себя. Числа времени под нагрузкой параллельных сессий гуляют в 3–6 раз — не публикуй ни одного времени без пометки, была ли нагрузка (я на этом попался дважды: §8 промта — про меня). 3. **Субагенты и копии пишут туда, куда смотрят**: перед каждым «финальным» прогоном — `git ls-files --others` и сверка sha256 копии с деревом. ### Правила, купленные тремя раундами ревью подряд — они теперь и в §5.1, но вот цена - **Закрытие проверяет НЕ тот, кто чинил.** Моя собственная проверка закрытия нашла 3 незакрытых и 7 частичных из 28 «исправленных»; ни одно моё «всё исправлено» за сессию не выдержало проверки. - **Сначала посадка, потом пин.** Дважды поймало ВАКУУМНЫЕ пины моего же авторства (пин, написанный первым, проверяет то, что и так верно). - **Худшие мои дефекты были НЕВЕРНЫМИ ДОВОДАМИ при верном коде** — комментарий здесь контракт, по которому следующая сессия действует. Довод без команды хуже отсутствия довода. ### Подтверждения, чтобы ты не тратил раунд на пере-открытие - Репро §4.5 и §4.9(а) — честные, не оспаривай, воспроизводи и чини. - «Семантика §4.1 не краснит ни один тест» — правдоподобно и по моему знанию тестов: всё, что я запинил про стоп, запинено через РЕЗУЛЬТАТ двери, не через условие гашения. - `FINDINGS`: вход через §26; §§0–13 — улика первой приёмки, §§14–35 — мои; дописывай НОВЫМИ секциями. - Каталог мутаций: запись `G-byte-gate` — записанный ВЫЖИВШИЙ с доводом недостижимости; не «чини» её. - Обмен с оркестратором закрыт (прямой канал, 27.08), четыре факта оттуда: (1) ратификация нового класса полосы «запись не завершена» — НА НЁМ, тем же лендинг-актом, что эрраты к 191/203(з); тебе — только движковая сторона (значение, номер, один класс на оба исхода с пофайловой правдой в отчёте); (2) стендовые `book.yaml` с ключами снимает он/владелец — не твоя зона; его замер «42 файла, ЧИТАЕМЫХ отклонений от конвенции ноль» сходится с моим; (3) **заказ §8 УЖЕ ИСПОЛНЕН мной до компакта**: слово `idle` в `Makefile` было неверным (мой замер шёл под тремя параллельными сессиями), довод переписан на диапазон ~95 с (свободный хост) … ~560 с (три сессии) без привязки к машине; ложное «на незагруженной машине» в `FINDINGS` §31 тоже исправлено — НЕ делай этот пункт заново, только перепроверь; (4) ⚠ отчёт сплошного читателя, на котором стоит §4.4 (2110/442/117/325), **в репозиторий не попал** — числа промта без первоисточника в дереве; будешь на них опираться — пере-мерь сам, это признал сам оркестратор. — бэкенд-сессия паков, до компакта