# Бэкенд-пак: РЕШЁННОЕ НЕ ПЕРЕ-СПРАШИВАЕТСЯ — движок платит за ответ, который у него уже есть > ✅ **АРХИВ — ПАК ОТРАБОТАН И ПРИНЯТ 17.09: акт `D39.266`** (лендинг `d25e418`, **32 пути**, $0 — платных > вызовов моделей пак не делал). Решённая движком строка переживает покупку: носитель — таблица > `bank_basis`, миграция v19, аддитивная. Ряд **490** закрыт по трём условиям, ряд **130** получил ТРЕТИЙ > исход (замысел достигнут другим механизмом, стоп-решение остаётся за владельцем), два отказа сданы с > замером, заведён ряд **496**. Числа приёмки: кампания **315/315 поймано, выживших 0, неизмеренных 0**; > M1 — **67 из 69** и **64 из 66** обслужено из памяти. > **Инструкции отсюда НЕ ИСПОЛНЯТЬ** (правило архива): живое — в акте `D39.266` и в рядах **492** · **496**. > ⚠ Якоря этого файла целились в строки, которые пак и был заказан переписать: они мертвы ПО ПОСТРОЕНИЮ и > чинить их нельзя — пере-наводка «под то, что в цели оказалось» есть подгонка под зелень. > Выдача №14 очереди №23. Зона — `backend/`. Платных вызовов моделей пак НЕ делает: $0. > ⚠ **Числа и адреса этого промта сняты мной по дереву 17.09. Те, на которых ты строишь решение, пере-снимай сама** — > заимствованное число улики не заменяет. За смену адреса в моих источниках уезжали семь раз, а два указывали > в НЕСУЩЕСТВУЮЩИЕ файлы. ## 1. Какая проблема и что решит твой результат На каждой покупке глав движок заново платит банк-ролям — терминологу и классификатору — за термины, которые уже решены. Замерено на двух купленных прогонах: на подписи сменились **12 из 69** и **14 из 66** передач, и словом, а не регистром, — 10 из 12 и 12 из 14. При этом собственная улика не менялась у **25 из 26** строк: мы платим за пере-решение без новой информации, а ответ может разойтись с тем, что уже стоит в отгруженных главах. Цена конкретна. Потолки банк-ролей **пожизненные** — `budget_usd: 1.00` и `classify_budget_usd: 1.00` (`backend/configs/pipeline-c1.yaml`); исчерпание необратимо, и книга остаётся без консолидированного банка навсегда, о чём не говорит ни одно сообщение (ряд **356**). Длинная книга при сегодняшней пере-покупке съедает потолок примерно за 6–16 покупок. Твой результат: строка, решённая движком, переживает покупку — её ответ берётся из носителя, а не покупается снова, и пере-спрашивается она только при названном событии. ## 2. Зона записи и git Пишешь `backend/` и свою секцию в `docs/PROGRESS.md`. **Не коммитишь** — дерево передаёшь мне, лендинг мой. Чужие незакоммиченные файлы не трогаешь. `.env` не читаешь никогда. ## 3. Карта чтения — ЗАКОН, пять позиций 1. `CLAUDE.md` в корне — канон проекта целиком. 2. `backend/internal/pipeline/terminologist.go` — **платный путь банк-ролей: сборка кандидатов, фильтр решённых (`dropBankSettled`, вызов на `:401`), батчи, разбор ответа и то, что делается с ним дальше.** Читать целиком, включая докблок `:1347-1395`: там записано, ПОЧЕМУ сегодняшний фильтр устроен из двух условий, и эта причина — твоя главная опора и главное ограничение. 3. `backend/internal/pipeline/mining.go` — эмиссия дельты, авто-банк и **граница сигнатурной остановки**. 4. `docs/architecture/18-bank-ontology.md` — что такое банк, чьё слово в нём живёт, и три правила дисциплины носителей. Страница короткая, читать целиком: твой пак её правит. 5. `docs/research/35-bank-memory-consilium.md` — §1.2 (П1), §1.5, §2.0. ⛔ **Читать как ПРОЕКТ, не как ратифицированное:** документ сдан БЕЗ сходимости, правилом остановки; свёртка N-2 в N-1 сделана после последнего круга проверки и **не судилась ничем**; четыре минора судьи на момент сдачи внесены не были; часть адресов писана по копии кода от 15.09 и указывает в файлы, которых нет. ## 4. Что построить ### 4.1. Замер M1 — ПЕРВЫМ БЛОКОМ, и второй блок без моего ответа не начинается Форма предиката без замера не принимается — это условие §4 итога консилиума, и оно не формальность: предикат решает, за что мы платим. Отпечаток и предикат делай чистыми функциями. Прибор — env-гейченный тест по образцу корпусных пинов (`D39.255` п.2), который гоняет их **над копиями** купленных баз и печатает: кандидатов · решённых · пере-спрошенных по классам (родня подписанных · нерешённые `auto` · новые · сменилась улика). **Два контроля обязательны, и оба различающие:** - подменить исходник вокруг ОДНОГО вхождения одного кандидата ⇒ пере-спрошен ровно он и его юнит; - два прогона под одним базисом ⇒ состав батчей байт-идентичен. ⚠ Контроль «два одинаковых прогона дали 0 % пере-спроса» ВЫРОЖДЕН: он меряет детерминизм харнесса, а не силу предиката. Не засчитывай его. ⛔ **Материал замера — НЕ ОДНА БАЗА, и первая редакция этого блока называла не тот файл.** Из базы отпечаток не считается: таблицы чанков в схеме НЕТ (контроль: 17 и 16 таблиц, `chunks` среди них ноль). Нужны ПЯТЬ входов: **(1)** копия базы платного прогона; **(2)** исходник `source.txt` рядом с ней; **(3)** `book.yaml` того прогона; **(4)** зеркало конфигов прогона; **(5)** контраст майнинга. ⛔ **Какая база платная — проверено замером, а не именем каталога.** Прогон **A** — это `/home/ubuntu/tm-coldrun-a/stand/books/bk_ROEHZBD46ALFI43E/project.db` (глоссарий **69** — число, на котором стоят все замеры банка; чекпойнтов 33; Σ`cost_usd` **0.419423**, сходится с ратифицированной суммой). ⚠ `evidence/v16-readonly/project.db` платным прогоном A **НЕ является**: `book_id` `guzhenren-coldrun-v16`, глоссарий 53, Σ 0.43611 — другая книга и другие деньги. Прогон **B** — `evidence/paid-final-project.db`, он же `stand/books/bk_NH6275ZIRFFTLZHG/project.db` (66 · 34 · 0.396657); дополнительно `evidence/paid-bankstop/project.db` годится как «проход 1 ДО подписи» (глоссарий 0, банка в нём нет). ⛔ **Ловушка конфигов, которая портит замер МОЛЧА.** Все `book.yaml` прогонов ссылаются на `/home/ubuntu-26/…`, и этого каталога на машине нет. Работай на КОПИИ `book.yaml`, перенаведя `pipeline`, `models`, `langpack_root` на `/home/ubuntu/tm-coldrun-{a,b}/mirror/cfg/…` (у A реестр моделей и языковые пакеты — в `freeze/backend/configs/`). Иначе загрузка либо падает, либо берёт конфиги ГОЛОВЫ, и ты меряешь не тот прогон. ⚠ **Вход контура, которого в репозитории нет вовсе:** `backend/configs/pipeline-c1.yaml:139`=`contrast_path: mining-contrast.zh.txt` — в git ноль файлов, в зеркалах прогонов он есть (349 046 строк) и резолвится ОТНОСИТЕЛЬНО каталога pipeline-файла. ⚠ Базы в WAL-режиме: копируй `project.db` вместе с `-wal` и `-shm`, печатай их размеры — копия одного файла при непустом WAL есть устаревший снимок. ⚠ `source.txt` — сырые байты gb18030; гони чанки через ingest движка (автопроба кодировки у него есть), а не читай файл харнессом как UTF-8, иначе кандидаты станут мусором и контроль покраснеет не по своей причине. ⛔ **Сырьё куплено и невосстановимо. Работай на КОПИЯХ**, оригиналы не открывай вовсе. База прогона A несёт схему 16 при голове 18: `OpenReadOnly` её отвергает, а `Open` МИГРИРУЕТ (ряд **475**). Я пере-снял: миграции v17/v18 аддитивны — `CREATE TABLE IF NOT EXISTS` и `ALTER TABLE … ADD COLUMN`, ни одного `DROP`/`RENAME`/`DELETE FROM`, — то есть на копии `Open` безопасен, а оригинал не открывай вовсе. `sqlite3` как CLI на машине НЕТ: python3 + модуль `sqlite3`, режим `mode=ro`. Числа блока 1 — в отчёт. **Блок 4.2 не начинается, пока я не принял форму предиката.** ### 4.2. Базис — ИСТОЧНИК ОТВЕТОВ, а не фильтр. Это несущее, и здесь самая тихая регрессия пака Соблазн описать N-1 как «фильтр снимает решённого кандидата» — и этого НЕДОСТАТОЧНО. Проверено чтением: снятый кандидат не попадает в консолидированные, а `attachConsolidatedDst` стампит передачу только при непустом значении — её докблок говорит дословно: «Terms the role did not answer are left untouched» (`backend/internal/pipeline/terminologist.go:1291`=`Terms the role did not answer are left untouched`). Дальше двухрежимная эмиссия ставит `draft` ТОЛЬКО при непустом `dst` (`backend/internal/miner/miner_emit.go:279`=`st.Dst, st.Status = m.Dst, "draft"`), иначе строка остаётся `auto`. ⇒ Наивный фильтр молча теряет КОНСОЛИДИРОВАННУЮ передачу и статус. ⚠ Механика уточнена пингом исполнителя 17.09 и пере-снята мной — прежняя редакция писала «обнулит передачу», это верно по следствию и неточно по механизму: `backend/internal/miner/miner_emit.go:268`=`st.Dst = props[0].Dst` ставит передачу из СЫРОГО банкнотного предложения ещё до консолидации, а статус поднимается отдельной веткой только при консолидированном значении. ⇒ снятая строка откатывается к догадке ПЕРВОГО куска в статусе `auto`; целиком передача исчезает лишь там, где своего предложения на поверхности не было. **И это опаснее обнуления, а не мягче:** пустой `dst` заметен на листе и в проекции, а строка с чужой догадкой в статусе `auto` выглядит нормальной и едет дальше как обычная неотвеченная. Прогон при этом выглядит дешевле, а банк беднеет — класс, за который здесь бьют: выгода видна, потеря молчит. ⛔ **Отсюда прямое требование к базису, и довод берётся у самого кода:** `backend/internal/miner/miner_emit.go:274`=`The mode turns on the CONSOLIDATED dst only` объясняет, что режим включается только консолидированной передачей и никогда сырой догадкой куска, иначе «первая мысль модели становится рабочим каноном книги». ⇒ базис подставляет КОНСОЛИДИРОВАННЫЙ ответ, а не любой непустой — иначе он сам сделает то, против чего эта ветка написана. **Поэтому базис подставляет ответ** — передачу, тип, род — там, где роль не спрашивали. Перечисли в записке ВСЕХ читателей ответа и покажи в отчёте, что каждый получил своё; начни с `attachConsolidatedDst`, `attachClassifiedGender` (`backend/internal/pipeline/terminologist.go:1306`=`attachClassifiedGender stamps`), их соседа по типу, `consolidatedRows`, строк стоп-таблицы и проекции консолидации. ⛔ **И ДВА читателя, которых докблок фильтра называет ПРИЧИНОЙ своей двух-условности — мой первый список их упускал, нашёл опровергатель промта.** `backend/internal/pipeline/terminologist.go:1352`=`CanonConflicts and ConsolidationKeyConflicts` говорит прямо: роль спрашивают о банкованных поверхностях НАРОЧНО, потому что её ответ читают эти две проверки, и они ставят владельцу на лист отметку «книга уже называет термин иначе». Вызовы живые — `backend/internal/pipeline/terminologist.go:624`=`terminology.CanonConflicts(cands, out, canon)` и `backend/internal/pipeline/terminologist.go:646`=`membank.ConsolidationKeyConflicts(consolidatedRows(cands, out), bank)`. ⇒ **реши и объяви ЯВНО: судится ли подставленный базисом ответ этими двумя.** «Да» — отметки поедут на лист за передачу, которую в эту покупку никто не покупал, и поедут на КАЖДОЙ покупке. «Нет» — отметки исчезнут у населения, ради которого фильтр и сделан двух-условным. Молчание здесь — выбор вслепую, и он денежный. ⛔ **Проверок СПОРА три, а не две, и третья решает судьбу правила «семья — юнит».** Кроме двух названных есть `backend/internal/pipeline/terminologist.go:637`=`terminology.ConsolidationConflicts(cands, out)`. Все три читают карту передач и не знают, откуда передача взялась, денег не тратят. ⭐ Довод в пользу «да, судить»: пере-спрошенный член семьи виден в конфликте с РЕШЁННЫМ братом только если брат присутствует в карте — иначе правило «семья решается целиком» лишается единственного $0-прибора, который его стережёт. Цена «да» — не деньги, а шум на листе подписи, и лечится он ЯРЛЫКОМ строки («решено памятью с покупки N»), а не молчанием. ⛔ **И потому базисной строке нужен СВОЙ флаг, а не существующий «снято банком».** Код объясняет старый флаг дословно: `backend/internal/pipeline/stopcontest.go:70`=`there is no answer to doubt` — строку, снятую банком, не оспаривают, потому что ответа нет. У базисной строки ответ ЕСТЬ, и отметка спора на ней законна. Унаследуешь флаг по совпадению смысла — молча выключишь спорность там, где она должна работать. Счётчик тоже свой: цикл счёта идёт по оплаченным. ⛔ **Отдельно `Invented`:** `backend/internal/pipeline/mining.go:502`=`row.Invented = dst != "" && !proposedByDrafts(dst, c.Variants)` — передача, подставленная базисом, черновиками ЭТОЙ покупки не предлагалась и будет помечена «выдумано движком»: ложное обвинение собственного канона книги на листе подписи. Смежное, назови оба: `backend/internal/pipeline/stopcontest.go:75`=`if r.SettledByBank` молча исключает такую строку из очереди спорных, а счётчик `backend/internal/pipeline/terminologist.go:618`=`res.Consolidated++` сменит смысл, если базисные ответы влить в общий набор до цикла счёта. Список мой — проверь его полноту сама, я уже дважды не увидел читателя, которого называет сам код. ### 4.3. Две границы записи — ДЕЛАЙ РОВНО ТАК, потому что одной недостаточно по построению Базис пишет ПРОГОН на своих выходных границах, и границ **две**: - `run-finished` — `backend/internal/pipeline/bookrun.go:258`=`r.exportBank(ctx, "run-finished")`, и она стоит под `if err == nil` (`backend/internal/pipeline/bookrun.go:254`=`if err == nil {`); - `bank-mining/signature-stop` — `backend/internal/pipeline/mining.go:270`=`bank-mining/signature-stop`. Одной мало не по вкусу, а по коду: сигнатурная остановка возвращается ЗНАЧЕНИЕМ-ОШИБКОЙ — `backend/internal/pipeline/waverun.go:185`=`return nil, &WaveSignatureStop{` (тип объявлен `:89`, метод `Error()` на `:104`). ⇒ прогон, кончившийся стопом подписи, базиса бы не написал вовсе, и резюм после подписи оплатил бы весь проход. ⛔ **Чего делать НЕЛЬЗЯ: писать базис сразу после платного прохода, до развилки.** Базис, присутствующий на краш-резюме, сжимает состав батчей, чекпойнты не находятся, и резюм перекупает то, что уже оплачено. **Место в порядке записи названо, потому что на границе стопа порядок — инвариант:** карта → таблица стопа → проекция банка → **базис** → память флажка. ⚠ **Это отклонение от буквы консилиума (он ставил базис пятым, последним), и довод взят у самого кода — нашёл опровергатель промта.** Комментарий над записью памяти флажка формулирует правило: `backend/internal/pipeline/mining.go:271`=`a crash between the two costs one benign re-stop` — последней пишется запись, чья потеря ДЕШЕВЛЕ. Потеря памяти флажка стоит одного лишнего доброкачественного стопа; потеря базиса стоит пере-покупки банк-ролей из ПОЖИЗНЕННОГО потолка (ряд **356**). ⇒ базис идёт раньше. Выберешь иначе — назови, чем громкая строка компенсирует окно краша, и запини это. Ветка отказа по форме такая же, но громче по последствию: **стоп остаётся стопом, а неудача записи базиса обязана КРИЧАТЬ** — она означает, что резюм пере-спросит роли, то есть это денежное событие, а не строка бухгалтерии. ⚠ **Писатель ОДИН — прогон; границ у него две.** Формулируй именно так. Онтология судит носитель с двумя писателями так же строго, как проекцию без читателя (`docs/architecture/18-bank-ontology.md:70-72`=`Отсюда правило таблицы выше`), и консилиум на этом уже спотыкался. ### 4.4. Форма отпечатка — РЕШИ САМА, но развилку знай: от неё зависит, переживёт ли пак следующую работу Отпечаток — хеш того, что читает роль. Консилиум пишет «строки `ctx`». ⛔ **Так делать опасно**, и вот почему: `ctx` — это окна KWIC, а их отбор, число и ширина управляются тремя независимыми ручками (`AttachKWIC`, `backend/internal/terminology/terminology.go:340`=`func AttachKWIC`; вызов `backend/internal/pipeline/terminologist.go:310`=`terminology.AttachKWIC(cands, chunks`; ключи `kwic_per_term`/`kwic_width` — пар-данные). Любая из них молча обнулит набор решённых у КАЖДОЙ книги: отпечатки не совпадут ни у одного кандидата, и книга потратит остаток пожизненного потолка на пере-консолидацию. Это не гипотеза: стратифицированный отбор окон — заказанная работа (ряд **449**, контроль: `stratif` — **0** совпадений при 53 не-тестовых Go-файлах двух пакетов), и она придёт. ⇒ Ищи форму, **инвариантную к способу выбора окон**: текст вокруг вхождений ключа, а не то подмножество, которое отобрали для роли. ⛔ **Но ширину отпечатка НЕ зашивай константой в Go** (моя первая редакция велела именно это — поймал опровергатель): `D39.50` п.4 ратифицировал, что «`kwic_width` в РУНАХ = утечка пары в общий слой» и вынес ширину в пар-данные, построено `D39.51`/`D39.53`, носитель — `backend/internal/lang/langpack.go:744`=`kwic_per_term`. Константа в Go стала бы единственной шириной и была бы пар-слепой по случайности, причём на ДЕНЕЖНОМ предикате. ⇒ ширина — пар-данное с движковым умолчанием, и в отчёте ответь на ревью-вопрос канона: заработает ли пара, которой в репозитории нет, без правки Go. ⛔ **И условие, без которого пар-данное само становится миной: ключ ОТДЕЛЬНЫЙ, не `kwic_width` и не `kwic_per_term`.** Возьмёшь ширину провода — первый же «богаче контекст» пере-откроет решённое у КАЖДОЙ книги пары, то есть вернётся ровно та связка, которую этот блок и запрещает. Инвариантность даёт то, что отпечаток не читает ОТОБРАННЫЕ окна; ручка ШИРИНЫ хеш двигает, и это говорится прямо, а не умалчивается. Эффективная ширина входит в версию схемы в заголовке носителя, чтобы смена пар-данного была НАЗВАННЫМ событием с числом, а не тихим обнулением. ⚠ И уточнение к сторожу исходника: при отпечатке по всем вхождениям дописка в конец меняет отпечаток только у строк, чьи вхождения попали в дописанное, — базис целиком не обнуляется. «Дописано» от «подменено» отличает хеш префикса прежней длины, и это $0. Якорь канона бери **по кандидату**, не по батчу: `CanonFor` принимает батч (`backend/internal/terminology/terminology.go:856`=`func CanonFor(batch []Candidate`), и отпечаток по батчу вернул бы зависимость от состава, от которой пак как раз уходит. В отпечатке НЕТ вариантов черновиков, `freq` и `since_ch`. ⚠ **Посылка «исходник книги приходит целиком» — ОТКРЫТЫЙ вопрос владельцу от 17.09, а не допущение пака**, и это поправка опровергателя к моей первой редакции. Шапка журнала решений говорит: для РАСТУЩЕГО сериала посылка ложна по построению, и вопрос вынесен владельцу отдельно. ⇒ сторож «хеш исходника разошёлся — кричим» у растущей книги срабатывал бы на каждом выпуске и обнулял базис целиком, то есть молча отвечал бы за владельца. Заголовок носителя всё равно несёт версию схемы отпечатка и хеш исходника, но сторож обязан **различать «исходник дописан в конец» и «исходник подменён»**: в первом случае решение старых строк падать не обязано. Не строишь различение — объяви растущую книгу вне скоупа ЯВНО и напиши, что механизм делает при разошедшемся хеше. ### 4.5. Условие «решено» — и состояние, которое движок считает невозможным Предикат требует: строка движка со статусом **`draft`** и непустой передачей · отпечаток совпал с записанным · то же верно для всех членов её юнита. ⛔ **Статус спрашивать ОБЯЗАТЕЛЬНО, «непустой передачи» мало.** Майнер производит пару `status:auto` с непустым `dst`: `backend/internal/miner/miner_emit.go:268`=`st.Dst = props[0].Dst` — ветка при `props[0].Via == ""` статуса не трогает, и он остаётся `auto`. Четыре комментария движка объявляют эту пару невозможной; пина нет ни одного. Это ряд **487**, твой пак его НЕ чинит — но посылку «решено ⇒ draft» запини, иначе починка ряда 487 завтра скачком двинет набор решённых, то есть деньги. ⚠ **И про «юнит целиком» знай ограничение.** Семьи и серии детектируются НАД `paid`, то есть ПОСЛЕ фильтра (`backend/internal/pipeline/terminologist.go:486`=`terminology.DetectFamilies(paid, fp)`, блок 483–487), и комментарий `:481-482` объясняет причину: детектировать над полным набором значит рассогласовать юниты с батчами, из них построенными. ⇒ это развилка, а не приказ: либо юнит для предиката считается отдельным проходом, не трогая батчер, либо «семья целиком» сужается и это называется. Решение — в записку-план. ### 4.6. Носитель — развилка, и я её НЕ закрываю за тебя Класс ратифицирован: «ПАМЯТЬ РЕШЕНИЙ» — писатель прогон, момент граница, читатель предикат до первой траты (эррата 17.09-д в шапке `docs/architecture/05-decisions-log.md`). Форму носителя класс не фиксирует. - **Файл рядом с базой** — буква консилиума (`bank-basis.yaml`). Образец готов: `backend/internal/pipeline/mining.go:703`=`return r.Book.ProjectDB + ".auto-bank.yaml"`, запись атомарная (`backend/internal/pipeline/artifact.go:26`=`func writeFileAtomic`). - **Таблица хранилища** — аддитивная миграция по образцу памяти флажка: денежный предикат не читает файл, который правится рукой; запись атомарна и идёт в своей транзакции. Образец — `backend/internal/store/stopmemory.go:33`=`func (s *Store) MarkStopPresented` (55 строк: «dumb storage», предикат живёт в пайплайне, набор только РАСТЁТ и удаления нет намеренно). ⚠ **Общей транзакции с чекпойнтами у образца НЕТ** — моя первая редакция приписала ему эту гарантию, поймал опровергатель (контроль: слово `checkpoint` в файле — 0 вхождений). Нужна такая транзакция — это НОВОЕ свойство, его строят и называют ценой, а не наследуют. Назови выбор и довод в записке-плане. ⛔ Отпечаток в СТРОКЕ авто-банка — мёртвая форма: `writeAutoBank` в продакшне зовётся ровно один раз, внутри ветки «не останавливаемся» (`backend/internal/pipeline/mining.go:242`=`r.writeAutoBank(ctx, mined, proposals` под `if !stopping` на `:235`), то есть на стопе подписи ему негде лечь. ### 4.7. Что пак задевает наружу — поле контракта Сегодня `never_asked` в проекции равно числу снятых банком (`backend/internal/pipeline/bankexport.go:118`=`NeverAsked: t.BankSettled`) и означает «сид держит поверхность и все черновики согласны». Поле живёт в каноне (`docs/architecture/14-api-contract/openapi.yaml`) и читается платформой в десятке мест. ⇒ строки, решённые базисом, в это поле класть НЕЛЬЗЯ без смены смысла: заводи **аддитивное** поле, это минор контракта, и назови его в отчёте — пинг платформе даю я. ⛔ **И носителей этого смысла БОЛЬШЕ ОДНОГО: три фразы солгут дословно, если их не тронуть** (находка опровергателя). Экран подписи: `backend/cmd/tmctl/render.go:282`=`the bank already renders those surfaces and every` (сводка) и `backend/cmd/tmctl/render.go:328`=`NOT ASKED: the bank already renders this surface` (построчно); текстовый сайдкар: `backend/internal/pipeline/mining.go:579`=`nothing to decide`. Для строки, решённой базисом, обе половины фразы ложны — банк поверхность не держит, и черновиков об этом не спрашивали. Это ровно класс «сообщение лжёт о собственной причине», который код уже запрещает у соседа (`D39.93` п.2). ⚠ И отметка `SettledByBank` на шов НЕ едет вовсе: проекция несёт `Invented`, `Contradicts`, `BankHolds` (`backend/internal/pipeline/bankexport.go:251`=`Contradicts: row.Contradicts, BankHolds: row.BankHolds`) и её не переносит ⇒ аддитивное поле проекции и есть единственный шовный носитель нового состояния. ### 4.8. Хвост, который сбрасывается без вреда для пака - **Ряд 490 — страж РАЗРЫВА окон глав у банковых строк.** Загрузчик ловит пересечение, разрыв не ловит никто; готовый образец для другой сущности — `backend/internal/membank/memvoice.go:271`=`func VoiceWindowGaps`. Бери у образца не только форму, но и гарантию: намеренный разрыв законен, поэтому исход — громкая строка, а не отказ загрузки. $0. - **Ряд 447** — подписанная поверхность снова спрашивается у роли во втором проходе. ⚠ Это смена продуктового поведения: сегодняшний фильтр платит за разнобой черновиков НАРОЧНО (докблок `:1350-1357` объясняет, зачем). Берёшь — объявляй как смену поведения, а не как починку. - **Ряд 448** — предупреждение о разовой цене после N-1 станет лгать. ⛔ **ИСПР.: моя первая редакция писала «текста в коде УЖЕ НЕТ, 0 совпадений» — это ЛОЖЬ, найденная опровергателем.** Текст жив дословно: `backend/internal/pipeline/terminologist.go:541`=`This is a ONE-TIME cost; every later run replays for $0` (он в хвосте длинной строки — я обрезал вывод по ширине и прочёл обрезок как факт). Устарел только адрес ряда (`:498`). **Носителей ТРИ:** сама строка · голден каталога операторских сообщений `backend/internal/pipeline/testdata/operator-messages.txt:136`=`terminology: this book had already paid`, который гейтит отдельный тест · два пина `backend/internal/pipeline/banksettled_test.go:193`=`func TestAResumeFindsItsCheckpointsAfterTheFilter` (резюм: ноль обращений к провайдеру, $0, и вдобавок ОТСУТСТВИЕ ключа `reconsolidated=true` в логе) и `:417` (обе половины: молчать на свежем прогоне, сработать в описанном случае). ⇒ пере-формулировка обязана обновить И голден: это не подгонка под зелень, а обслуживание заказанной смены поведения (`D39.183`), и она ОБЪЯВЛЯЕТСЯ в отчёте. ### 4.9. Что в пак НЕ входит — чтобы молчание не читалось отказом **N-3** (одна строка закона на ключ) — нужен замер M2, ряд **477**. **Стратифицированный отбор** (ряд **449**) — ⛔ гейт `D39.50` НЕ снят: слово владельца дало санкцию на замер M4, а не разрешение строить; я исправил это в трекере и плане сегодня. **N-4** и **Б-5** — ПОСТРОЕНЫ паком `D39.257`, заказом больше не являются. **N-6** (журнал-телеметрия) — гейчен разморозкой фронта, ряды **214** и **221**. **N-5** — закрыт `D39.263`: производитель — человек через сид. **Точечная перегенерация** (`D39.205` п.5) — банковый пак её не трогает, и молчание здесь не отказ. ### 4.10. Стандарт работы — слово владельца, ратифицировано `D39.258` п.3 Чистый код, рефакторинг там, где он нужен, никаких заплаток, техдолга не копить. Мерило проекта — **качество художественного перевода длинной книги**; механизм строится там, где несёт качество или деньги. ## 5. Самопроверка ИСПОЛНЕНИЕМ Гейт зоны (`make battery`) обязателен и самопроверкой **не является**. Сверх него — адверсариальный проход по своей готовой работе; субагентов поднимать разрешаю явно. Глубину выбираешь сама, направление даю я — пять мест, где этот пак мягок: 1. **Потеря передачи у снятого кандидата** (§4.2) — самая тихая регрессия. ⛔ Пин сравнивает **ПАРУ (передача, статус)**, а не одну передачу: сравнение по одному полю пройдёт ЗЕЛЁНЫМ на строке, которая уехала с консолидированной передачи на сырую догадку куска, оставшись в статусе `auto`. Уточнение пришло пингом исполнителя и подтверждено деревом. 2. **Точка записи** (§4.3) — посадка, ставящая базис до развилки, обязана краснеть пином резюма. 3. **Отпечаток** (§4.4) — посадка, меняющая число или ширину окон, НЕ должна обнулять набор решённых. 4. **Первый резюм после записи базиса** — он платит за переупаковку хвоста один раз. ⛔ **Пин обязан различать ДВЕ оси, и одну мою редакцию опровергатель снял как повторяющую ровно ту ложь, которую велит убрать ряд 448:** (1) резюм ТОЙ ЖЕ покупки — $0 со второго; (2) СЛЕДУЮЩАЯ покупка, где решилась хоть одна новая строка, — хвост батчей переупаковывается и платится снова. Вторая ось — та, ради которой пак существует («строка переживает ПОКУПКУ»), и формулировка «каждый последующий прогон за $0» запрещена. 5. **`auto`-хвост** — строка, которую модель устойчиво считает не-термином, платится КАЖДУЮ покупку на тождественном входе. Это цена, названная консилиумом; напечатай её числом. ⚠ **Мутации:** новые посадки вводи в гейт, иначе «поймана» — разовый прогон смены, а не рубеж проекта. Полный прогон каталога после рефактора обязателен. ⛔ Гнать его **в переднем плане**: гейт движка не помещается в фоновую задачу харнесса (ряд **488**), и это не про нехватку памяти машины. ## 6. Оси ревью Две-три, можешь заменить с аргументом: **денежная** (ни один путь не покупает дважды и мимо бюджета) · **сохранность ответов** (ничего уже купленного не потеряно — §4.2) · **рефакторинг без регресса** (что переехало и чем доказано, что поведение прежних вызывающих не изменилось). ## 7. Записка-план До первой правки кода — короткая записка в свою секцию журнала: схема носителя и выбор его формы с доводом · форма отпечатка · таблица читателей ответа · что решила по юнитам (§4.5) · денежная семантика резюма. ## 8. Заявление = команда Любое число и любая категорика отчёта идут **с командой, которой они получены**, — так, чтобы я повторил их у себя, не переспрашивая. «Кампания зелёная», «мутации пойманы», «резюм бесплатен» без команды — утверждение О проверке, а не проверка: читатель видит слово вместо числа. ⚠ Рядом с любым НУЛЁМ печатай контрольную величину, доказывающую, что вопрос задан существующему предмету: «ноль строк» и «нет такой таблицы» в выводе неразличимы и оба выглядят успехом. ⚠ Приёмка пере-ранит выборочно, и расхождение числа с его же командой дороже, чем отсутствие числа. ## 9. Эхо-протокол старта **Первым действием** — десять строк **своими словами**: скоуп, инварианты, чего не делаешь. Дословный пересказ промта подтверждает канал, но не понимание. ## 10. Что не удалось — и где прибор слеп Секция «не удалось / не проверено» обязательна. ⚠ И вторая половина: **«где мой прибор слеп и я это знаю»** — что невидимо, почему не чинила, чем закрывается. Норма зоны: **в коде названо, в отчёте нет — значит для следующей смены не названо.** ## 11. Канал вопросов, право отказа, советчик Конфликт промта с кодом — **пинг**, не интерпретация: промт мог устареть между написанием и чтением, и за смену это случалось. Право сказать «этого делать не надо» с аргументом у тебя есть, и два пункта этого пака я жду скорее отказанными с замером, чем построенными наспех. Тебе разрешён **один** старший советчик (`model: "fable"` явно) для развилки, которую не закрывает своё суждение; досылай вопросы ему, а не поднимай новых — его контекст и есть ценность. Он советчик, не источник истины: его посылки проверяются деревом. ⚠ Мой собственный советчик по этому паку дал девять замечаний, и проверка деревом подтвердила не все: одно его предложение спорило с ратифицированным доводом в коде. ## 12. Прямой канал Механизм — `CLAUDE.md` §«Связь между сессиями»; здесь он не повторяется, но **исполняется первым делом**: впиши свой блок в `/tmp/textmachine-channel` (роль · имя из `ListAgents` · ссылка на ЭТОТ файл · дата · что делаешь и чего не трогаешь), дописывая в конец и не трогая чужие блоки. Адрес оркестратора бери оттуда же, и туда же шли эхо из блока 9 — оно и служит проверкой канала. ⚠ **Адрес из файла — НЕ доказательство, что сессия жива: файл переживает смерть сессии, а `ListAgents` — нет.** ⚠ **Нужной роли в файле нет, файла нет или имя не отвечает ⇒ КАНАЛА НЕТ, и это НОРМАЛЬНЫЙ случай: НЕ опрашивай сессии подряд.** Вопрос — секцией в свой отчёт, работа продолжается; владелец прочитает и пере-передаст. ## 13. Критерий завершённости — проверяемый - у **каждого** пункта заказа исход: сделано · не делаю с доводом · пинг; - **числа блока 4.1 предъявлены и форма предиката принята мной** до того, как написан носитель; - круги **сошлись**: последний не дал новых находок, прежние закрыты таблицей «находка → что сделано → **чем предъявлено**»; - таблица мутаций полная, выжившие названы поимённо, полный прогон каталога пройден; - **список «что отрефакторено и почему»** — либо явное «рефакторинга не потребовалось» с доводом; - числа сняты **после последней правки**, у каждого команда повторения; всё живое — **в дереве**; - гейт зоны зелёный **целиком** (сверка списком целей, а не отсутствием слова FAIL); - сказано явно: **«работа завершена, править не планирую»**. ## Приложение. Ряды, на которые пак опирается Строит: **130** · **490**. Задевает и обязан назвать: **330** · **438** · **440** · **461** · **466** · **481** · **487**. Условия работы: **475** (купленные улики) · **488** (гейт в переднем плане) · **356** (пожизненный потолок). ⚠ **Ряд 478 — ЛАТЕНТНЫЙ, и называть его без оговорки нельзя** (поправка опровергателя): его посылка опровергнута замером зоны 17.09 на настоящем батчере — снятие решённого кандидата даёт **0 сдвигов ординала из 246 снятий** при положительном контроле 18 из 19, боевые конфиги размер батча не задают. ⇒ ключ покупки не трогать; перекупка первой покупки после базиса идёт от смены СОДЕРЖИМОГО батча, а не ординала. Условие оживления класса — мелкий `batch_runes`. ⛔ **Две ноты 17.09 — условия работы твоего пака, и обе про деньги ровно тех ролей, которые он фильтрует.** `D39.264` вынес петлю повторной попытки в общий контур и подключил банк-роли к вердикту; `D39.259` включил `gates.terminology.regenerate: 1` во ВСЕХ трёх боевых конвейерах (`backend/configs/pipeline-c1.yaml:193`=`regenerate: 1`, `backend/configs/pipeline-arm-glm.yaml:72`=`regenerate: 1`, `backend/configs/pipeline-arm-mistral.yaml:79`=`regenerate: 1`). ⇒ на батч возможна ВТОРАЯ платная ступень, и промт обязывает тебя назвать: **чей ответ ложится в базис, если батч куплен лестницей** (ответ выигравшей ступени, не первой) · не ломает ли базис посылку пина `backend/internal/pipeline/bankladder_test.go:111`=`func TestAHealthyBankTableIsNeverAskedAgain` («здоровая таблица покупается ровно раз»), чья посылка утверждается отдельной строкой — `backend/internal/pipeline/bankladder_test.go:131`=`Gates.Terminology.Regenerate` — и без неё пин измерял бы ОТСУТСТВИЕ механизма, о чём его докблок предупреждает сам · входит ли ступень в «$0 на резюме». Не входят, но названы: **447** · **448** · **449** · **477** · **214** · **221**. ⚠ Ряд **489** — ревизия документации твоей зоны, тринадцать дней без исполнителя: четыре пункта измерены живыми, пять не измерены. Возьми в работу то, что дёшево, и назови остальное.