diff --git a/docs/BACKEND_SEAM_FIX2_SESSION_PROMPT.md b/docs/BACKEND_SEAM_FIX2_SESSION_PROMPT.md new file mode 100644 index 00000000..0c92f63e --- /dev/null +++ b/docs/BACKEND_SEAM_FIX2_SESSION_PROMPT.md @@ -0,0 +1,533 @@ +# Промт: БЭКЕНД ДОФИКС-2 «ВХОДНАЯ ДВЕРЬ ШВА» — флажок становится флажком, точка фиксации отчёта, фикс-лист приёмки + +> Выдан оркестратором №19, 25.08.2026. Активный промт зоны бэкенда — один; этот заменяет +> `BACKEND_SEAM_FIX_SESSION_PROMPT.md` (отработан, уезжает в архив при лендинге). +> Параллельно законен только `platform/docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md` (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` — «93 % дефолта». Приёмка померила дважды: **92.8 с** в батарее и +**97.6 с** изолированно. Явление настоящее, правку не откатывай — но **довод перепиши на верные числа +и без привязки к машине**: репозиторный файл не должен говорить «на этой машине». + +## 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), + **в репозиторий не попал** — числа промта без первоисточника в дереве; будешь на них опираться — + пере-мерь сам, это признал сам оркестратор. + +— бэкенд-сессия паков, до компакта diff --git a/docs/README.md b/docs/README.md index 64f044c7..4cae8842 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,7 +22,7 @@ | Роль | Активный промт | Статус | |---|---|---| | Оркестратор | [ORCHESTRATOR_SESSION_PROMPT.md](ORCHESTRATOR_SESSION_PROMPT.md) | роль и нормы; счётчик роли — CURRENT-STATE | - | Бэкенд | [BACKEND_SEAM_FIX_SESSION_PROMPT.md](BACKEND_SEAM_FIX_SESSION_PROMPT.md) | дофикс движковой половины входной двери шва по приёмке 25.08: блокер round-trip reject-документа + восемь мажоров + пять слепых пятен гейта; дерево пака НЕ закоммичено и продолжается, не начинается заново; запуск по слову владельца | + | Бэкенд | [BACKEND_SEAM_FIX2_SESSION_PROMPT.md](BACKEND_SEAM_FIX2_SESSION_PROMPT.md) | дофикс-2 входной двери шва: флажок банка становится флажком В ДВИЖКЕ (отложенная половина строки 191б — «ослабить границу паузы»), единая точка фиксации отчёта, фикс-лист приёмки; дерево двух паков НЕ закоммичено и продолжается; запуск по слову владельца | | Платформа | [PLATFORM_P8_REVIEW_SESSION_PROMPT.md](../platform/docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md) | read-only ревью четырёх осей; запуск по слову владельца | | Полигон | [POLYGON_EXP2223_REDO_SESSION_PROMPT.md](POLYGON_EXP2223_REDO_SESSION_PROMPT.md) (отложенный — [POLYGON_PACKAGE4_SESSION_PROMPT.md](POLYGON_PACKAGE4_SESSION_PROMPT.md), строка 85) | фаза Д ИДЁТ; ⚠ живой носитель курса — в `eval/dovodka/`, какой именно называет зона (⚠ [POLYGON_PHASE_D_HANDOFF.md](POLYGON_PHASE_D_HANDOFF.md) — перекрытый снимок, читать не как курс) | | Фронт | активного НЕТ | **ЗОНА ЗАМОРОЖЕНА** (D39.136 п.2 + D39.147: разморозка отдельным словом владельца, не привязана к P7); перечень первого касания — в зонном журнале |