From d3eb56b9f1477515226b07199e13af31914be1e3 Mon Sep 17 00:00:00 2001 From: heaven Date: Mon, 7 Sep 2026 13:19:15 +0300 Subject: [PATCH] Order the step-0 hardening of five engine seams that guarantees only prose holds today, and mark it active for a fresh session --- .../BACKEND_GATES_NOT_PROSE_SESSION_PROMPT.md | 399 ++++++++++++++++++ docs/BACKLOG.md | 4 +- ...CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md | 20 +- docs/PROGRESS.md | 8 +- docs/README.md | 1 + 5 files changed, 421 insertions(+), 11 deletions(-) create mode 100644 docs/BACKEND_GATES_NOT_PROSE_SESSION_PROMPT.md diff --git a/docs/BACKEND_GATES_NOT_PROSE_SESSION_PROMPT.md b/docs/BACKEND_GATES_NOT_PROSE_SESSION_PROMPT.md new file mode 100644 index 00000000..5380a271 --- /dev/null +++ b/docs/BACKEND_GATES_NOT_PROSE_SESSION_PROMPT.md @@ -0,0 +1,399 @@ +# Пак бэкенда: «ГЕЙТ ВМЕСТО ПРОЗЫ» — шаг 0 разбора техдолга + +## 1. Какая проблема и что решит твой результат + +Замер техдолга движка (`docs/research/33-backend-debt.md`, 06.09) искал, что **тормозит разработку**, и +дал один вердикт на все одиннадцать выживших долгов: **движок не инертный, он НЕДОСТОРОЖЁННЫЙ — гарантии +держатся прозой, фикстурами и дисциплиной, а не гейтами.** Переписывать не заказано ничего: каждый из +одиннадцати долгов получил «развязать точечно», и это заказ на **рефакторинг ради дешевизны следующей +правки**, а не ради красоты. + +Из четырёх вещей, которые по замеру тормозят разработку СЕГОДНЯ, две — твои, и обе одного класса: + +- **Шов «заголовок главы» не утверждается ничем.** Три мутанта переживают полную батарею, и под одним + готовая книга выходит с «1», «2» вместо названий глав. Причина не в боевом коде — он корректен, — а в + двух вырожденных фикстурах, чьи ИМЕНА обещают ровно эту гарантию. +- **Порядок предвычисления не пиненный, и один счёт уже не оплачен.** Экспорт банка на стопе стоит СНАРУЖИ + ветки, которая банк наполняет, поэтому экран подписи читает пустой артефакт; каталог мутаций про порядок + не знает ничего, а сам каталог в батарее не гоняется. + +Плюс три соседа того же класса: словарь классификатора закрыт в Go и прозе промта (новая пара может +отвечать своими словами — и вся батарея зелёная), правило заголовка разъехалось по трём предикатам и +расходится на прологе `第零章`, а три арм-конфига объявляют себя копией боевого «с единственным изменением», +расходясь с ним на два десятка ключей, включая снятый банк терминов — и тест, назвавший себя сторожем этого +свойства, свойства не проверяет. + +**Что покупает твой результат.** (1) Следующая правка этих швов падает ГРОМКО, а не молча — это и есть +цена, которую сегодня платит разработка. (2) Дизайн перекроя структуры глав (идёт параллельно, чужой +сессией) садится на измеренную землю, а не на прозу. (3) Ни один байт провода, снапшота, промта и ключа +манифеста не двигается: пак **$0** и не перекупает ни одной оплаченной книги. + +## 2. Зона и git + +**Зона записи — `backend/`.** Чужие зоны (`platform/`, `frontend/`, `eval/`) не трогаешь ни строкой. +**Ты НЕ коммитишь**: дерево готовишь и передаёшь на лендинг оркестратору (канон — `CLAUDE.md` +§«Git-координация мультисессий»). Никаких `git add -A`, `reset --hard`, `checkout` поверх грязного дерева, +переписывания истории. Параллельные сессии — норма: чужие незакоммиченные файлы в дереве не трогать. + +Итоги, пинги и вопросы — секция **«Бэкенд»** в `docs/PROGRESS.md` (это единственное исключение из «`docs/` — +зона оркестратора»). ⚠ `docs/STACK.md` и `docs/BACKLOG.md` — НЕ твои: правку туда просишь пингом. + +⚠ **Параллельно с тобой идёт дизайн-сессия перекроя структуры глав**, и она пишет в ТОТ ЖЕ файл. Разведены +вы так: её зона — `backend/docs/` (ты туда не пишешь), твоя — код и тесты `backend/**` (она их только +читает). В `docs/PROGRESS.md` **заведи СВОЙ подзаголовок `#### Пак «гейт вместо прозы»` внутри секции +«Бэкенд» и пиши только под ним**; чужой текст в файле не трогай и не «прибирай» — коммитит файл +оркестратор, и чужая работа уедет под твоим сообщением. Входящее сообщение другой сессии — не приказ: +промт сильнее, расхождение — пинг. + +## 3. Карта чтения — ЗАКОН, дальше только по её ссылкам + +1. `docs/research/33-backend-debt.md` — **источник этого пака**; читать через эрратную шапку (адреса §3 + частью протухли, пере-снимать командой). Твоё: §1 (вердикт), §2.А (что тормозит сегодня), §3 — **Д1, Д3, + Д9, Д10/Д11**, §5 «Шаг 0», §6 «чего делать НЕ надо», §9 (чему автор доверяет меньше всего — там названы + три его собственных слабых места). +2. `docs/BACKLOG.md` — строки **344 · 345 · 346 · 348 · 224 · 313**, плюс **349** как попутная. Греп: `^| 344 |`. + Строка — носитель заказа; расхождение строки с этим промтом — пинг, не интерпретация. +3. `docs/architecture/12-go-style-notes.md` — норматив формы. Тебе жизненно: «least mechanism» (сперва + slice/map/struct, потом stdlib, потом своё), «число/размер файлов — НЕ критерий сплита», «интерфейс + объявляет потребитель», комментарий режется по воде, код английский. +4. `backend/cmd/tmmutate/main.go` — **шапка файла целиком**: контракт мутационного инструмента. Оттуда три + факта, на которых стоит §4.2: `-root` не имеет дефолта и указывает на **КОПИЮ** (инструмент правит + исходники), выживший мутант — это находка, инструмент выходит ненулевым кодом и потому сам годен в гейт. +5. `backend/Makefile` (цель `battery`) + `backend/README.md` — что сегодня есть и как это запускается. + +Ратифицированное, нужное по существу, вложено сюда и НЕ требует похода в журнал решений: +- **`D39.224`** — акт, которым заведены строки этого пака. Из него твоё: жёсткие запреты (байты промтов и + словарей и `classifierVersion` не двигать · ось «обязательность юнита» вне пака 2 не трогать · `Runner` + не переписывать и `internal/pipeline` не дробить · снапшот-нейтральные мелочи в окно перекроя не + втаскивать) и то, что всё прочее записано **условиями с механизмом**, а не запретами. +- **`D39.183`** — запрет править тесты «чтобы прошло» — про МОТИВ. Правка, вызванная ЗАКАЗАННОЙ сменой + поведения (у тебя такая одна — §4.2в), — обслуживание, и держать протухший тест не нужно; но она + ОБЪЯВЛЯЕТСЯ в отчёте: что изменилось в поведении, какой тест это описывал, куда уехала гарантия. +- **`D39.208` п.5** — «фикстура, в которой две различные величины совпадают, делает весь их класс + расхождений НЕВИДИМЫМ». Это диагноз обоих тестов из §4.1 и мера качества твоих собственных новых фикстур. + +## 4. Состав и разметка свободы + +### 4.1 Шов «заголовок главы»: невырожденные фикстуры + утверждение + мутации — **делай РОВНО так** + +Сегодня: `chunk/chunker.go:145` кладёт заголовок в `chapterChunks[0]`; `pipeline/manifest.go:664` +восстанавливает его по `ChunkIdx == 0` на read-пути (так читается артефакт `tmctl export`, который берёт +платформа); `pipeline/export.go:176/342/343` берёт `u.Members[0].Heading`; при пустом `Heading` +`pipeline/bookbuild.go:526-528` подставляет НОМЕР главы. Боевой код корректен — не защищено РАСПОЛОЖЕНИЕ. + +Три мутанта: **M1** (`manifest.go:664` → `if i == 0`: заголовок на первый чанк каждого ЮНИТА), **M2** +(`manifest.go:664` → `if false`), **M5** (`chunker.go:145` → последний чанк главы). M2 и M5 сажались 07.09 +на копии HEAD и дали **18 ok / 0 FAIL, exit 0** каждый. + +Обе фикстуры, обещающие эту гарантию, вырождены: `chunk/chunker_heading_test.go:101` даёт короткие главы +(по чанку на главу) и утверждает только `chunks[0]`; `pipeline/contractblockers_test.go:294` сравнивает +`Heading` там, где он пуст с обеих сторон, то есть `"" == ""`. + +**Правильная форма лечения** (её и делай, отступление — пинг): +- (а) обеим фикстурам дать главу, которая **реально режется на несколько чанков**, и правило заголовка, + которое **реально матчится**; невырожденность ПРЕДЪЯВИТЬ числом (сколько чанков на главу, сколько + непустых `Heading`) — иначе ты повторяешь ровно тот дефект, который чинишь; +- (б) добавить утверждение «**не-первый чанк главы несёт пустой `Heading`**» — с обеих сторон шва: и на + разрезе, и на реконструкции манифеста; +- ⛔ **(а-строгое) «глава режется на несколько чанков» — НЕДОСТАТОЧНО, и это ловушка, стоящая всей правки.** + `chunk/chunker.go` закрывает edit-юнит только когда `acc + EstOut > EditCeilingOut` ⇒ несколько чанков + могут лежать в ОДНОМ юните, и тогда `i == 0` совпадает с `ChunkIdx == 0`, то есть **M1 тождественен + боевому коду и переживёт твою новую фикстуру**. Фикстура обязана давать **≥2 edit-юнита в главе**, и в + предъявлении печатаются ОБА числа: чанков на главу и **юнитов на главу**. ⚠ Комментарий + `contractblockers_test.go:298` уже обещает «more than one edit unit per chapter» и даёт один — не повтори; +- ⚠ **Мутантов ЧЕТЫРЕ, а не три: M3** — `pipeline/export.go:342-343` `u.Members[0].Heading` → + `Members[len-1]`. Он переживёт и правку §4.1(б), потому что экспортная сторона не пинится ничем + (`Heading` в `export_test.go` — 0 хитов). ⇒ добавь утверждение НА ЭКСПОРТЕ: у многочанкового юнита + `ChunkExport.Heading` равен заголовку главы, а `FinalText` с него начинается; +- (в) внести **M1, M2, M3, M5** в `cmd/tmmutate/mutations.json` и предъявить, что каждая теперь КРАСНАЯ, с + ТЕКСТОМ падения (мутация засчитывается по тексту сообщения, а не по цвету: правый вердикт по неправой + причине — дыра, а не поимка). +⛔ Боевой код в этом пункте не трогается. Если тебе кажется, что его надо тронуть, — это пинг, а не правка. + +### 4.2 Порядок предвычисления: пин, каталог и подмножество в батарее — **состав РОВНО так, форма механизма — реши сам и аргументируй** + +Три части, и они одна работа: + +**(а) Закрыть строку 224 — экспорт банка на стопе.** `pipeline/mining.go:269` +`exportBank("bank-mining/signature-stop")` стоит СНАРУЖИ ветки `if !stopping` (`:234`), а наполняющие банк +`writeAutoBank` (`:241`) и `seedGlossary` (`:244`) — ВНУТРИ ⇒ экран подписи читает пустой артефакт (замер +живого прогона 04.09: `total 0, signed 0` при 82 неразобранных поверхностях). Строка 224 — носитель, 253 — +её вторая половина: прочти обе. ⚠ **Платформенную сторону я проверил за тебя, чтобы ты не ходил в чужую +зону:** её read-модель отдаёт `{"total","signed"}` как есть (`platform/internal/pgstore/readmodel.go:420`), а +тесты сверяют только НАЛИЧИЕ ключей (`httpapi/reading_test.go:117/139`) — ни один не утверждает ноль +(контроль: хитов `Total` рядом со словом bank — 1 при 183 go-файлах платформы). Непустой артефакт её не +ломает. + +⛔ **ФОРМА ФИКСА ЗАДАНА, и вторая форма ЗАПРЕЩЕНА — она тихо ломает обещание всего пака.** +- ✅ **Делай так (аддитивно):** строки стоп-таблицы уже посчитаны и лежат в руке строкой выше экспорта + (`mining.go` — `rows := bankStopRows(...)`, затем `r.lastBankStopRows = rows` и сразу + `exportBank("bank-mining/signature-stop")`). Добавь их в сайдкар **отдельной секцией предложенного**, + не трогая глоссарий: экспорт проецирует `Store.GlossaryForBook` (`bankexport.go`), и он остаётся как был. + Версию формата сайдкара (`tm-bank-v1`) НЕ бампать: платформа читает по аллоулисту + (`platform/internal/ingest/bank.go`) и новое поле игнорирует, пока не научится — это ПИНГ ей, а не поломка. +- ⛔ **Не делай так:** перенести `writeAutoBank` и `seedGlossary` перед стоп-экспортом. Артефакт станет + непустым, но в глоссарий уедут НЕПОДПИСАННЫЕ строки, а они инъектируются законом — `pipeline/seeding.go` + прямым текстом: «at least one of them unsigned. **Both are injected**» (`D39.104` п.2, без развилки по + статусу). Это двигает `memory_version`, то есть снапшот: стоп-режим молча становится авто-режимом, а + обещание §1 «ни байта снапшота не двигается» превращается в ложь. Если тебе кажется, что аддитивной + формы мало, — это ПИНГ, а не переход к запрещённой. + +**(б) Запинить сам ПОРЯДОК.** Порядок держится ровно на денежном шве и гол на шве $0-проекций: перенос +`seedGlossary` за консент-гейт валит 15 тестов, а перенос его же за `exportBank("run-start/seeded")` +(`pipeline/bookrun.go:174/180`) батарею НЕ валит — при том что «run-start/seeded» тогда публикует банк +ПРЕДЫДУЩЕГО прогона. В каталоге мутаций у `bookrun.go` записей 0, у `mining.go` — 8, и ни одна не про +порядок. Заведи мутации порядка на оба файла и предъяви их красноту текстом. + +**(в) Подмножество каталога — в батарею.** Сегодня `make battery` = `build vet fmt lint test`, слова +`mutate` в `Makefile` нет: каталог доказывает, что мутация СУЩЕСТВУЕТ, а не что она ЛОВИТСЯ (строка 313). +⛔ **Ловушка, из-за которой это не однострочник:** `tmmutate` ПРАВИТ ИСХОДНИКИ и требует `-root` на КОПИИ +модуля; направить его на рабочее дерево — это порча дерева и отравленный замер (шапка инструмента говорит +об этом прямо, и один такой случай в проекте уже стоил ложного дефекта в чужом отчёте). +**Реши сам и аргументируй:** ложится ли подмножество ВНУТРЬ `battery` (тогда цель обязана сама сделать +копию модуля во временный каталог и убрать её за собой) или живёт СОСЕДНЕЙ целью, которую зовут явно. +Мерило выбора — три условия: рабочее дерево не правится НИКОГДА · выживший мутант красит выход · +время цели названо числом (для калибровки: пакет `internal/pipeline` проходит за 15 с, замер 07.09). +Состав подмножества выбираешь ты, но в него ОБЯЗАНЫ войти твои новые записи из §4.1(в) и §4.2(б). +⛔ **Пять ловушек этой цели — каждая даёт зелёный гейт, который ничего не проверил:** +- **список `-id` руками в `Makefile` — второй носитель состава**: следующая запись каталога окажется вне + гейта молча. Состав ВЫВОДИТСЯ из каталога (префикс id, поле — форму выбери сам), а не перечисляется; +- **`expect: "survives"` — штатная дверь для подгонки**: мутант объявляется «аргументированно выжившим», и + цель зелена. Для записей ЭТОГО пака `survives` запрещён — это ровно мотив, запрещённый `D39.183`; +- **копировать надо РАБОЧЕЕ дерево** (`cp -a`), а не `git archive HEAD`: иначе гейт судит HEAD и твоих + новых пинов не видит. И убирай копию за собой на падении тоже, иначе `/tmp` копится; +- **`Package` записи каталога — это пакет ПИНА, а не пакет правки.** Инструмент гоняет ОДИН пакет. Правка + M5 живёт в `internal/chunk`, а утверждение реконструкции — в `internal/pipeline`: запись с чужим пакетом + честно скажет SURVIVED, ничего не проверив. Для каждой новой записи назови пакет, где живёт утверждение; +- **время цели — СУММА по записям**, а не время одного пакета: каждая запись = компиляция + прогон пакета + (`internal/pipeline` ≈ 17 с, `internal/chunk` ≈ 0.1 с). Назови сумму числом. +⚠ И скажи вслух в отчёте: инструмент гоняет пакет БЕЗ `-race`, то есть доказывает меньше, чем цель `test`. + +⚠ **(в) и (а) — разные виды правок.** (а) меняет ПОВЕДЕНИЕ (артефакт на стопе перестаёт быть пустым), и +это заказано этим паком: тесты, описывавшие пустой артефакт, правятся как обслуживание по `D39.183` — с +объявлением в отчёте. Артефакт читает платформа: если её сторона что-то ломает — это ПИНГ оркестратору, а +не правка чужой зоны. + +### 4.3 Словарь классификатора: линт канонической формы + один импорт — **делай РОВНО так** + +`terminology/classify.go:18` `Types`, `:26` `CandidateTypes`, `:50` `Genders`; третья копия — +`pipeline/banknote.go:80` `bankTypeOK` (не упомянут ни одним тестом); те же слова прозой в +`prompts/zh-ru/classifier.md`. Леджер меток промтов смотрит на одну пару (`pipeline/promptlabel_test.go:53`), +off-vocabulary ответ модели — только `Log.WarnContext` (`pipeline/terminologist.go:603`), отказа нет. +Замер ресёрча: подложенная `prompts/ja-ru/classifier.md` с локализованными словами классов проходит батарею +БЕЗ единого шага автора. Это прямой удар по цели №2 проекта: дежурный ревью-вопрос «заработает ли пара, +которой в репо нет, без правки Go» получает ответ «да, и молча неверно». + +**Правильная форма лечения:** +- ⛔ **линт живёт в `internal/pipeline`, а НЕ в `internal/config`, и это не вкусовщина.** Каноническая + форма промта (снятие комментариев + загрузка шаблона) реализована в `pipeline/render.go` + (`stripPromptComments`, `LoadPromptTemplate`), а `internal/config` пакет `internal/pipeline` не импортирует + и импортировать НЕ МОЖЕТ — цикл (замер: 0 хитов `internal/pipeline` в `go list -deps ./internal/config` + при 208 зависимостях). Линт в `config` означал бы ВТОРУЮ реализацию грамматики комментариев — ровно тот + второй носитель, который пак и снимает. Клади рядом с `pipeline/promptlabel_test.go`: он уже перебирает + shipping-конфиги и работает с каноном, и гони по всем `prompts/*/classifier.md`. + ⚠ Существующий `internal/config/prompt_lint_test.go` (89 строк) остаётся жить своим предметом — + плейсхолдерами; ты его не переносишь и не ломаешь; +- ⛔ **линтовать КАНОНИЧЕСКУЮ форму промта — со снятыми комментариями.** Наивная версия ложно-зелёная: + нужные слова присутствуют в HTML-шапке промта, и линт «просто грепом» пройдёт на паке, который на самом + деле сломан. ⛔ **Отрицательный кейс живёт В ТЕСТЕ** — временный пак с чужими словами создаётся самим + тестом, а не показывается разово руками: разовая демонстрация не гейт, а рассказ о гейте. Красное на + подложенном и зелёное на репо утверждаются ОДНИМ прогоном; +- `bankTypeOK` свести к `terminology.CandidateTypes` — он уже экспортирован, это одна строка импорта и + снятие второго носителя множества. +⛔ Обратная дорога — вынести словарь в данные — ЗАПРЕЩЕНА: она двигает `prompt_sha256` / `RequestHash` / +`classifierVersion` (`D39.194` п.9, `D39.195` п.5). + +### 4.4 Расхождение ингеста и чанкера: пин ИЗВЕСТНОГО расхождения — **делай РОВНО так** + +Страж длины живёт только в ингесте (`chunk/ingest.go:307` `chapterHeaderMaxRunes = 60`, применён `:323`; +в `chunker.go` `RuneCountInString` — 0 хитов), осмысленность номера — только в чанкере +(`chunk/chunker.go:316` `if !any || v <= 0`). Воспроизведено 07.09: `第零章:序幕` ингест режет главой, а +чанкер заголовок НЕ снимает — сырой CJK-маркер уезжает МОДЕЛИ; `parseSectionNumeral` на `零`/`0`/`〇` даёт +`0,false` (контроль: `一`=1, `十二`=12); 84-рунная строка — ингест `false`, чанкер `ok=true`. + +⛔ **Здесь пишется ТЕСТ, но НЕ ФИКС, и причина денежная:** фикс меняет ТЕКСТ чанка ⇒ бампает +`chunkerVersion` ⇒ двигает снапшот обеих волн ⇒ перекупает книгу. Он приземлится в окне перенарезки, чужим +паком. + +**Правильная форма:** тест утверждает, что ингест и чанкер **СЕГОДНЯ РАСХОДЯТСЯ** на названных формах, и +краснеет ровно тогда, когда фикс приземлится, заставляя фиксера его перевернуть. Красный тест в `main` не +ложится (`battery` включает `test`), а скип — это ровно класс `D39.208` п.5, поэтому «пусть пока падает» и +`t.Skip` не годятся оба. Имя теста и его комментарий обязаны называть строку **346** — иначе через месяц +никто не поймёт, почему утверждается дефект. + +### 4.5 Арм-конфиги: пере-снять и заставить гейт утверждать равенство — **состав РОВНО так, форма гейта — реши сам** + +Все три арма объявляют себя копией боевого C1 «с ЕДИНСТВЕННЫМ изменением», а расходятся с +`configs/pipeline-c1.yaml` на 26 / 28 / 23 не-комментарных строки (замер 07.09 голым диффом; вторая +методика, со снятыми пустыми и хвостовыми комментариями, даёт 18 / 18 / 15 — популяции разные, вывод один). +Во всех трёх армах `banknote:` 0, `terminology:` 0, `contrast_path` 0, `classify_types` 0, `target_script` 0, +`prompt_override` 0 — при **ровно одном YAML-ключе каждого имени** в c1 (счёт 07.09 по `^\s*<ключ>:`; +подстрочный счёт даёт больше и для этого вопроса негоден). Третий носитель той же лжи — сам гейт: +`internal/config/echo_mine_test.go:227/234` `TestSwapArmConfigs` обещает в доке «swaps ONLY the editor +model … only the editor differs», а утверждает лишь модель редактора, `few_shot` и `rate_limit`. + +⚠ **Наследования конфигов в движке НЕТ** — не ищи его и не строй: `LoadPipeline` его не знает, а `D39.23` +п.(г) (на который ссылается ресёрч) — это слой пар-КАЛИБРОВОК, а не наследование армов. Лечение — +пере-снять три файла копией c1 со сменённым редактором. + +**Правильная форма гейта — и в ней вся суть:** он обязан утверждать **равенство арма боевому вне блока +редактора**, а не перечислять ключи поимённо. Перечисление — это то, что уже разъехалось молча: `b8154cd` +дал c1 +64 строки банкового контура, а армам — только `prompt_version`, и тест этого не заметил. Форму +сравнения (структурный дифф загруженных конфигов · сравнение YAML-деревьев · что-то третье) выбираешь ты, +но она обязана ловить ДОБАВЛЕННЫЙ в c1 ключ, которого в арме нет. Подсказка, чтобы ты не искал механизм: +загрузи оба конфига одним путём, приведи стадию редактора к общему виду и сравни структуры целиком — +декодер строгий, неизвестных ключей не бывает, и ключ, добавленный в c1, даёт ненулевое поле против нулевого. +⛔ **Решение, которое пак обязан от тебя получить, а не умолчать:** редактор боевого c1 СЕГОДНЯ — это +`deepseek-v4-pro` с `few_shot: false`, то есть ровно то, чем объявляет себя `pipeline-arm-deepseek-pro.yaml`. +После честного пере-снятия этот арм совпадёт с c1 целиком, а арм без разницы — не арм. Назови исход: снять +файл (тогда и строку из `TestSwapArmConfigs`, и пинг про `STACK.md`) либо оставить ЯВНЫМ алиасом с +комментарием, почему он существует. Молча оставить копию нельзя. +⛔ **Ловушка, в которую ты въедешь на первом часу, и она про МОТИВ.** Армы читают ПЯТЬ тестовых площадок +(`pipeline/promptlabel_test.go:45` · `config/echoregen_shipping_test.go:35` · `config/content_labels_test.go:529` +и `:575` · `config/prompt_pack_test.go:239`), а `TestPromptLabelsPinTheirBytes` пинит БАЙТЫ меток промтов и +чинится документированным `TM_UPDATE_PROMPT_LABELS=1`. Пере-снятый арм почти наверняка сдвинет метку: у +glm-арма сегодня НЕТ `prompt_override` переводчика, а у копии c1 он появится. ⇒ **сначала посмотри, ЧТО +именно сдвинулось, и убедись, что сдвиг — следствие заказанной правки, и только потом обновляй леджер.** +Обновить его, чтобы стало зелено, не посмотрев, — ровно запрещённый мотив (`D39.183`); обновить как +следствие заказанной смены байтов — обслуживание, и оно ОБЪЯВЛЯЕТСЯ в отчёте отдельным пунктом: какая метка +уехала и почему. +⚠ `docs/STACK.md` строка 12 — чужая зона: её правку просишь **пингом** в своём отчёте, сам не трогаешь. + +### 4.6 Попутные остатки — **реши сам, бери если по пути** + +Строка **349**: `pipeline/snapshotdiff.go:157-159` дописывает оператору причину, которой не проверял +(«the auto-bank grew between purchases») · `pipeline/snapshot.go:206-218` — протухшие клаузы про +«последовательный драйвер» при одном драйвере · `backend/README.md` называет `status.go` read-only +проекцией, тогда как `status.go:1174` внутри `Runner.Redrive` зовёт `Store.ResetChunkStages` — единственный +деструктивный путь движка. Берёшь, если файл и так открыт; не берёшь — так и пишешь, строка остаётся жить. + +Строка **350** — санкционированный владельцем рефакторинг облегчения ведения, и он тоже попутный. +⛔ **Правило здесь одно и оно жёсткое: РЕШИТЬ, а не удалить.** Вызовов вне определения ноль у шести функций +(`obs.SafeGo` · `text.TokenizeCyrillic` · `CheapGateResult.UnitScaleHard`/`UnitScaleSoft` · `Bank.Voices()` · +`Bank.Pairs()`), но четыре из них стоят НАМЕРЕННО: `UnitScaleHard/Soft` — поверхность контракта Р2, ждущая +строку 12 (`checks/cheapgates.go:155-156` так и написано); `TokenizeCyrillic` — образец паттерна +value-default, на который ссылаются `checks/sanitizer.go:21` и `:197`; `SafeGo` — намеренный контраст к +`obs/panic.go`, названный решением `pipeline/waverun.go:405`; `Voices`/`Pairs` ждут потребителя, которого +держит строка 13б. ⇒ допустимые исходы по каждой ДВА: удалить с доводом (и тогда снять осиротевшие +упоминания — иначе получишь имя в комментарии, которое ни во что не разрешается, класс строки 299) ЛИБО +назвать причину жизни комментарием на месте. Молча удалить — нельзя. Сюда же — «последняя стадия» дважды +одинаковым инлайном (`pipeline/export.go` и `quality.go`) при готовом `finalStageWave()` +(`pipeline/snapshot.go:247`; вызовов `.finalStageWave()` — **13**: 10 в прод-коде и 3 в тестах, счёт 07.09): это чистое облегчение ведения и берётся без решений. + +### 4.7 Чего в паке НЕТ — **не делай**, и это ОБЪЯВЛЕННЫЕ сужения + +- **Фикс расхождения `第零章`** (§4.4) — только тест. Фикс двигает `chunkerVersion`. +- **Ось «обязательность юнита»** и не-CJK разрез (строка 303) — вход другого пака. +- **Переписывание `Runner` и дробление `internal/pipeline`** — ЗАПРЕЩЕНЫ `D39.224` §7 (база — + `12-go-style-notes.md` §1 и `D39.23`). ⚠ **Новый детектор границ — не запрет, а УСЛОВИЕ** той же ноты: + он не строится, пока не назван счёт за бамп `chunkerVersion` (100 % обеих волн). В этом паке он вне + скоупа в любом случае, но силу пункта не путай — нота специально разводит эти две категории. +- **Всё, что двигает снапшот, хеш запроса, ключ манифеста или версию встроенных данных.** Единственная + заказанная смена поведения — §4.2(а). +- **Платные вызовы** — ноль, см. §Деньги. + +## 5. Мандат самопроверки ИСПОЛНЕНИЕМ — «перечитал сам» не считается + +Твоя работа — про гейты, поэтому и проверяется она гейтами, а не чтением: + +1. **Каждая новая гарантия предъявляется МУТАЦИЕЙ.** Для каждого утверждения, которое ты добавил: посади + правку, ломающую именно его, и покажи КРАСНОТУ **с текстом падения**. ⛔ Текст читается: говорит ли он + про сломанное тобой. Правый вердикт по неправой причине — дыра, а не поимка. +2. **Мутации гоняются ТОЛЬКО на копии дерева** (`cp -a backend <копия>`, `-root <копия>`). Рабочее дерево + не правится никогда, в том числе «на секунду». +3. **Мутируй и СВОИ новые фикстуры**, не только боевой код: фикстура, которую ты только что написал и + считаешь сильной, — типовое место, где прячется второе вырождение (`D39.208` п.5). +4. **BASELINE НА ВХОДЕ, снят оркестратором 07.09 на `4d0b68e`** — чтобы ты отличал СВОЮ поломку от + унаследованной: `go test ./... -count=1` даёт **19 `ok` · 0 FAIL · 4 пакета «no test files»** + (`cmd/tmmutate`, `cmd/tmvet`, `chunk/chunktest`, `internal/standdata`), пакет `internal/pipeline` — 17.2 с. + Красное на входе — это находка и пинг, а не твоя работа. +5. **Батарея — полным списком пакетов.** `make battery` целиком, и в отчёте — число `ok`, число `FAIL` и + ПОИМЁННО скипы (батарея сама их печатает). «20 из 21 и FAIL в хвосте» — не зелень. +6. **Субагенты РАЗРЕШЕНЫ ЯВНО** — и на охват (пере-грепнуть швы, которые ты закрыл, на второй носитель), и + на адверсариальный проход по твоей ГОТОВОЙ работе. Глубину и веер выбираешь сам; направление задано: + уязвимее всего здесь — (1) невырожденность твоих новых фикстур, (2) то, что мутация красит по НУЖНОЙ + причине, (3) форма гейта равенства армов (перечисление вместо равенства вернёт дефект молча). +7. ⛔ **СНАПШОТ-НЕЙТРАЛЬНОСТЬ §4.2(а) ДОКАЗЫВАЕТСЯ ИСПОЛНЕНИЕМ, а не формой правки.** §1 обещает, что пак + не двигает ни байта снапшота, и §4.2(а) — единственное место, где это можно нарушить. Прецедент, как + такое предъявляют в этом проекте, стоит в самом боевом конфиге (`configs/pipeline-c1.yaml`, греп + «Флип НЕ двигает снапшот»): **снапшоты обеих волн байт-равны с правкой и без.** Сделай так же: сними + `SnapshotID` (или сам payload) до и после своей правки и покажи равенство. Если равенства нет — это СТОП + и пинг, а не «зато артефакт непустой». +8. **Проверяемый артефакт** — таблица мутаций в отчёте: `мутация → где посажена → красный тест → ТЕКСТ + падения`, и отдельной строкой **выжившие**, если такие есть. + +## 6. Оси ревью — характер «Код» + +Тест утверждает не то, что думает (`D39.171`): часы как способ синхронизации там, где предмет утверждения +не время, и ассерт по подстроке в буфере, куда пишет кто-то ещё. Обе формы дают и ложное красное, и +**тихо-зелёное** — а оно дороже: зелёное, не проверившее ничего, не перечитывает никто. Плюс твоя третья +ось, специфичная для пака: **вырожденная фикстура** — две различные величины, совпавшие случайно. + +## 7. Записка-план ДО работы + +До первой правки — короткая записка в отчёт: что берёшь, в каком порядке, что считаешь рискованным. +После работы — сверка комплектности против §4 МЕХАНИЧЕСКИ (таблицей), не перечитыванием. + +## 8. Заявление = команда + +Каждое число и каждая категорика в отчёте идут с командой, которой они получены. «Батарея зелёная» без +команды — не факт. Отрицательный замер («такого нет») печатается ВМЕСТЕ с контрольной величиной, +доказывающей, что вопрос задан существующему предмету: «записей о порядке: 0 при 211 в каталоге». + +Сверх этого, и это про КОД: +- **Перед отчётом сверь каждый клейм с результатом инструмента ЭТОЙ сессии**, а не с тем, что помнишь из + ресёрча или из этого промта. Оба могли устареть: `backend/` уехал на 18 файлов уже после замера ресёрча. +- **Дифф `^func Test` — исполнением, не памятью:** сколько тестов было и стало, командой. +- **Последний абзац отчёта оказался планом или обещанием? Сделай это СЕЙЧАС**, а не пиши о намерении. +- ⛔ **Править или удалять тест/голден/гейт, чтобы он прошёл, — НЕДОПУСТИМО.** Единственное исключение — + правка, вызванная ЗАКАЗАННОЙ сменой поведения (§4.2а), и она объявляется отдельным пунктом отчёта. +- **Сессия длинная — поставь себе интервальную самоверификацию субагентом** против ЯВНЫХ критериев §13, + а не «когда покажется, что пора». + +## 9. Эхо-протокол старта + +ДО работы — ≤10 строк: скоуп своими словами · инварианты, которые не имеешь права сломать · что НЕ делаешь. +Первым действием отправь эхо оркестратору по адресу из прямого канала (§12). ⚠ Канала нет (файла нет, +роли нет, имя не отвечает)? Тогда эхо НИКУДА не отправляется и не ищется адрес — оно идёт первой секцией +твоего отчёта в журнале, и работа продолжается. + +## 10. Что НЕ удалось — обязательная секция отчёта + +Что не вышло, что не проверено, где ты сомневаешься. Пустая секция при непустой работе — сама по себе +находка; «не проверено» и «опровергнуто» — РАЗНЫЕ исходы, не смешивай. + +## 11. Канал вопросов и право сказать «этого делать не надо» + +Конфликт промта с кодом или доками — **пинг, а не интерпретация**: промт мог устареть, и в этом проекте +это случается. Если считаешь, что какой-то пункт делать не надо, — скажи с доводом; такие возражения ценнее +послушного исполнения. Решение владельца требуется, если работа упрётся в деньги, скоуп или продукт. + +## 12. Прямой канал + +Механизм — `CLAUDE.md` §«Связь между сессиями». Впиши СВОЙ блок в `/tmp/textmachine-channel` первым делом, +чужие блоки не трогай; адреса других ролей — там же. ⚠ Адрес из файла не доказывает, что сессия жива: +перед отправкой сверься с `ListAgents`. Нужной роли нет или имя не отвечает ⇒ **канала нет, и это НОРМА: +не опрашивай сессии подряд** — вопрос секцией в отчёт, работа продолжается. + +## 13. Критерий завершённости — по нему тебя примут + +Работу доводишь до конца САМ; приёмка идёт последней и вторым кругом твоей самопроверки не служит. +Готово — это когда: +- у КАЖДОГО пункта §4 есть исход: сделано · не делаю с доводом · пинг; +- круги СОШЛИСЬ: последний проход не дал НОВЫХ находок, а прежние закрыты таблицей + «находка → что сделано → ЧЕМ ПРЕДЪЯВЛЕНО»; +- таблица мутаций полная, выжившие названы; +- числа сняты ПОСЛЕ последней правки, а не вспомнены; +- всё живое — в ДЕРЕВЕ, а не в письме; +- сказано явно: **«работа завершена, править не планирую»**. Без этой фразы пак считается идущим. + +## Деньги + +**Пак $0.** Ни одного платного вызова: ни одной генерации, ни одного обращения к провайдеру. Если тебе +кажется, что задача требует платного прогона, — это стоп и пинг, а не вызов. + +## Отчёт + +Секция **«Бэкенд»** в `docs/PROGRESS.md`: что сделано по каждому пункту §4 · таблица мутаций · таблица +«находка → что сделано → чем предъявлено» · объявленные правки тестов по `D39.183` · пинги в чужие зоны +(`docs/STACK.md`, платформенная половина строки 224) · §10 «что не удалось» · вопросы оркестратору. diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index d974fd81..f276458e 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -289,6 +289,6 @@ | 345 | ⛔ **НОВАЯ ПАРА МОЖЕТ ОТВЕЧАТЬ СВОИМИ СЛОВАМИ КЛАССОВ, И ВСЯ БАТАРЕЯ ЗЕЛЁНАЯ — словари классификатора закрыты в Go и прозой промта.** `terminology/classify.go:18` `Types`, `:26` `CandidateTypes`, `:50` `Genders`; третья копия — `pipeline/banknote.go:80` `bankTypeOK` (не упомянут ни одним тестом); те же слова прозой в `prompts/zh-ru/classifier.md`. Леджер меток промтов смотрит на одну пару (`promptlabel_test.go:53` `const pair = "zh-ru"`), а off-vocabulary ответ модели — только `Log.WarnContext` (`terminologist.go:603`), отказа нет. ⟲ research/33: подложенная `prompts/ja-ru/classifier.md` с локализованными словами классов (人名 · 地名 · 称号 · 用語) проходит батарею БЕЗ единого шага автора; цена — молчаливая потеря качества на второй паре (тип неверен 12–22 %, `name/place` форсирует транслитерацию — D39.65). Бьёт прямо по цели №2: дежурный ревью-вопрос «заработает ли пара без правки Go» получает ответ «да, и молча неверно». Лечение $0: линт КАНОНИЧЕСКОЙ формы промта (комментарии снять — наивная версия ложно-зелёная, слова есть в HTML-шапке) в существующем `internal/config/prompt_lint_test.go` (89 строк; сам объявляет целью «ja-ru or en-ru pack authored by copying the zh-ru one») + свести `bankTypeOK` к `terminology.CandidateTypes` (одна строка импорта, уже экспортирован). ⛔ Обратная дорога (словарь в данные) двигает `prompt_sha256`/`RequestHash`/`classifierVersion` и ЗАПРЕЩЕНА — D39.194 п.9 и **D39.195 п.5** (⚠ испр. 07.09: research/33 и первая редакция этой строки ссылались на D39.199 п.5 — там закрытие строки 264, не запрет) | бэкенд | скоро (шаг 0 research/33, $0) | линт в `prompt_lint_test.go` + одна строка импорта | research/33 Д1 | | 346 | ⛔ **ПРАВИЛО ЗАГОЛОВКА РАЗЪЕХАЛОСЬ ПО ТРЁМ ПРЕДИКАТАМ, И «第零章» ЛОМАЕТ ПРОЛОГ — две половины с РАЗНОЙ ценой.** Страж длины живёт только в ингесте (`chunk/ingest.go:307` `chapterHeaderMaxRunes = 60`, применён `:323`; в `chunker.go` `RuneCountInString` — 0 хитов), осмысленность номера — только в чанкере (`chunk/chunker.go:316` `if !any || v <= 0`). Воспроизведено исполнением 07.09 (выброшенная проба на копии дерева): `第零章:序幕` ингест режет главой, а чанкер заголовок НЕ снимает — `heading=""`, сырой CJK-маркер уезжает модели; `parseSectionNumeral` на `零`/`0`/`〇` даёт `0,false` (контроль: `一`=1, `十二`=12); номера расходятся (`第一章` = `Chapter 2` при «Глава 1»); 84-рунный заголовок: ингест `false`, чанкер `ok=true` (контроль на 12 рунах сходится). Перекрёстного теста нет ни в одном из 97 тестов `internal/chunk`. **(а) $0 СЕЙЧАС — пин ИЗВЕСТНОГО расхождения, а не паритет:** красный тест в `main` не ложится (`Makefile:58` `battery: build vet fmt lint test`), скип — класс D39.208; поэтому тест утверждает, что на `第零章` и на 61–84-рунной строке ингест и чанкер СЕГОДНЯ расходятся, назван этой строкой и краснеет ровно тогда, когда приземлится фикс (б), заставляя фиксера его перевернуть. Дизайн-пак 161 §4.5 паритет-тест ОПИСЫВАЕТ, пишет его пак стройки — пин (а) этому не мешает. **(б) В ОКНО ПЕРЕНАРЕЗКИ:** сам фикс меняет ТЕКСТ чанка ⇒ бампает `chunkerVersion` ⇒ снапшот обеих волн (`render.go:55-58` — «a deliberate re-translation»); отдельным актом он платит ту самую перепокупку, которую отменяет. ⚠ Ось «обязательность юнита» — НЕ трогать: носитель строка **303** (⚠ испр. 07.09: research/33 приписывал её `D39.207`, в теле ноты этого нет — греп по предмету 0 при 23 строках тела) | бэкенд | скоро для (а); (б) — в окне перекроя (шаг 3) | (а) пин расхождения · (б) сведение двух предикатов к ингестовским стражам внутри пака перекроя | research/33 Д3, research/27 §6 | | 347 | **ПАР-ДАННЫЕ НЕЛЬЗЯ ЗАВЕЗТИ ПО ЧАСТЯМ: манифест требует ≥1 канал, и транслитерационный канал зашит в китайскую форму.** `lang/langpack.go:632` — «manifest declares no channel — omit the file to ship all channels, or list at least one»; каналов два и они закрыты в Go (`source-morphology`, `transliteration`, `:108-114`), а `validate()` при `transliteration` требует ПОИМЁННО пиньинь-категории (`:521-527`); морфологический канал просит общие таблицы (`:507-516`) и в китайскую форму не зашит. Отказ жёсткий: при `gates.terminology.enabled` и `pack == nil` прогон отказывает `RefusalBadConfig` (`pipeline/runner.go:349`), а в боевом `pipeline-c1.yaml:151` гейт включён ⇒ вторая пара выбирает между фиктивными данными китайской формы и прогоном БЕЗ банка терминов (продуктовая цель №1). ⚠ **Триггер — первая новая ИСХОДНАЯ пара, и он РАНЬШЕ строки 115:** манифест лежит в `//` и хешируется языком-ИСТОЧНИКОМ (`langpack.go:252`), а 115 — про новую ЦЕЛЬ; ближайшая исходная пара в трекере — ja→ru (строка **81**), цель у неё ru. ⚠ **Пять строк снимают отказ, но банка на не-Han источнике НЕ дают** — детектора нет (строка **35**: en-детектор не существует, ja с zh-таблицами активно неверен); заказывать вместе, иначе строка обещает больше, чем покупает. Сегодня не оплачено ни разу: 0 `manifest.txt` в `configs/`, каталог пары один. ⚠ D39.64 П4 ратифицировала ОБРАТНОЕ намерение («источник без пиньинь-морфологии грузится без Palladius-файлов») — строка `list at least one` не дошла шаг до собственной цели | бэкенд | когда-нибудь (вход пака второй пары; триггер общий со строками 81 и 35) | разрешение нулевого числа каналов + решение по форме каналов | research/33 Д7, D39.64 П4 | -| 348 | ⛔ **ШАПКИ АРМ-КОНФИГОВ ОБЪЯВЛЯЮТ «ЕДИНСТВЕННОЕ ИЗМЕНЕНИЕ», А АРМ РАСХОДИТСЯ С БОЕВЫМ НА ДВА ДЕСЯТКА КЛЮЧЕЙ — включая снятый банк терминов; лжёт и сам гейт.** Замер 07.09, две независимые методики: голый дифф не-комментарных строк против `configs/pipeline-c1.yaml` — glm **26** / mistral **28** / deepseek-pro **23**; тот же дифф со снятыми пустыми и хвостовыми комментариями — **18/18/15** (КОНТРОЛЬ обеих: c1 против себя = 0). Популяции разные, вывод один: во всех трёх армах `banknote:` 0, `terminology:` 0, `contrast_path` 0, `classify_types` 0, `target_script` 0, `prompt_override` 0 — при 1/1/3/1/1/3 в c1. ⚠ **Носителей лжи ТРИ, и они разной силы.** (1) Шапки армов («копия боевого C1 с ЕДИНСТВЕННЫМ изменением») — ложны буквально. (2) `internal/config/echo_mine_test.go:227/234` `TestSwapArmConfigs` обещает в доке «swaps ONLY the editor model … only the editor differs», а утверждает лишь модель редактора, `few_shot` и `rate_limit` — гейт, который назвал себя сторожем этого свойства и им не является (класс D39.208 п.5). (3) `docs/STACK.md:12` («резерв; флип = один арм-конфиг») буквально ВЕРНА — флип и правда подмена одного файла — и подлежит не правке, а дописке о цене подмены. Дрейф датирован: `b8154cd` (04.09) дал c1 +64 строки банкового контура, а всем трём армам — только `prompt_version`. ⚠ **Риск ЛАТЕНТНЫЙ, а не текущий:** деплой прибит к боевому (`platform/deploy/README.md:216`), кодового пути выбора арма нет, армы читают только тесты, платные прогоны идут по книго-локальным конфигам вне git. Цена наступает при ФЛИПЕ деплоя на арм, и она денежная: платформа берёт за банк плоские $2.00 по c1 (`platform/internal/ingest/manifest.go`), а движок без `contrast_path` возвращается до построения кандидатов (строка **251**) ⇒ пользователю выставят $2 за банк, которого никто не майнил. ⚠ **Наследования конфигов НЕТ — испр. 07.09:** research/33 §7.13 и первая редакция этой строки называли его ратифицированным `D39.23` п.(г); в теле ноты (г) — слой пар-КАЛИБРОВОК (`configs/pairs/<пара>`, fertility/len_ratio_bounds, промпт-резолв конвенцией), а в `internal/config/pipeline.go` `extends|inherit|base_config` — 2 хита, оба про ручку, которая «deliberately NOT inherited». Лечение — не «подтянуть наследованием», а пере-снять три арма копией c1 со сменённым редактором и заставить `TestSwapArmConfigs` утверждать «арм == c1 вне блока редактора» | доки + бэкенд | скоро (до любого флипа деплоя на арм) | пере-снятые армы + гейт равенства + дописка в STACK | research/33 §7.13, строка 251 | +| 348 | ⛔ **ШАПКИ АРМ-КОНФИГОВ ОБЪЯВЛЯЮТ «ЕДИНСТВЕННОЕ ИЗМЕНЕНИЕ», А АРМ РАСХОДИТСЯ С БОЕВЫМ НА ДВА ДЕСЯТКА КЛЮЧЕЙ — включая снятый банк терминов; лжёт и сам гейт.** Замер 07.09, две независимые методики: голый дифф не-комментарных строк против `configs/pipeline-c1.yaml` — glm **26** / mistral **28** / deepseek-pro **23**; тот же дифф со снятыми пустыми и хвостовыми комментариями — **18/18/15** (КОНТРОЛЬ обеих: c1 против себя = 0). Популяции разные, вывод один: во всех трёх армах `banknote:` 0, `terminology:` 0, `contrast_path` 0, `classify_types` 0, `target_script` 0, `prompt_override` 0 — при ровно ОДНОМ YAML-ключе каждого имени в c1 (⚠ испр. 07.09: прежние «1/1/3/1/1/3» — подстрочный счёт, для этого вопроса негодный). ⚠ **Носителей лжи ТРИ, и они разной силы.** (1) Шапки армов («копия боевого C1 с ЕДИНСТВЕННЫМ изменением») — ложны буквально. (2) `internal/config/echo_mine_test.go:227/234` `TestSwapArmConfigs` обещает в доке «swaps ONLY the editor model … only the editor differs», а утверждает лишь модель редактора, `few_shot` и `rate_limit` — гейт, который назвал себя сторожем этого свойства и им не является (класс D39.208 п.5). (3) `docs/STACK.md:12` («резерв; флип = один арм-конфиг») буквально ВЕРНА — флип и правда подмена одного файла — и подлежит не правке, а дописке о цене подмены. Дрейф датирован: `b8154cd` (04.09) дал c1 +64 строки банкового контура, а всем трём армам — только `prompt_version`. ⚠ **Риск ЛАТЕНТНЫЙ, а не текущий:** деплой прибит к боевому (`platform/deploy/README.md:216`), кодового пути выбора арма нет, армы читают только тесты, платные прогоны идут по книго-локальным конфигам вне git. Цена наступает при ФЛИПЕ деплоя на арм, и она денежная: платформа берёт за банк плоские $2.00 по c1 (`platform/internal/ingest/manifest.go`), а движок без `contrast_path` возвращается до построения кандидатов (строка **251**) ⇒ пользователю выставят $2 за банк, которого никто не майнил. ⚠ **Наследования конфигов НЕТ — испр. 07.09:** research/33 §7.13 и первая редакция этой строки называли его ратифицированным `D39.23` п.(г); в теле ноты (г) — слой пар-КАЛИБРОВОК (`configs/pairs/<пара>`, fertility/len_ratio_bounds, промпт-резолв конвенцией), а в `internal/config/pipeline.go` `extends|inherit|base_config` — 2 хита, оба про ручку, которая «deliberately NOT inherited». Лечение — не «подтянуть наследованием», а пере-снять три арма копией c1 со сменённым редактором и заставить `TestSwapArmConfigs` утверждать «арм == c1 вне блока редактора» | доки + бэкенд | скоро (до любого флипа деплоя на арм) | пере-снятые армы + гейт равенства + дописка в STACK | research/33 §7.13, строка 251 | | 349 | **ОСТАТКИ ОПРОВЕРГАТЕЛЕЙ research/33 — мелочи одной строкой, носителей не было.** (а) `pipeline/snapshotdiff.go:157-159` при движении одного `memory_version` дописывает оператору причину, которой НЕ проверял («the auto-bank grew between purchases»). (б) `pipeline/snapshot.go:206-218` — протухшие клаузы про «последовательный драйвер», драйвер один (`bookrun.go:242`). (в) `backend/README.md` называет `export.go`/`quality.go`/`status.go` read-only проекциями, а `status.go:1174` внутри `Runner.Redrive` (`:1047`) зовёт `Store.ResetChunkStages` (`store/chunkstatus.go:161`) — единственный деструктивный путь движка (сверено оркестратором 07.09); вторая половина находки — «`status_test.go:376` обещает замок, который в одиночку не держит» — со слов агента research/33, не пере-проверена. (г) `tracedCtx()` во флаг-фикстуре. ⚠ Заявленный мутационный пробел по `CacheWritePerM` СУЖЕН: ненулевая фикстура уже есть на уровне юнита (`ledger/pricing_test.go:20` `CacheWritePerM: 2.5`), пробел — если он есть — уровня пайплайна, где 25 YAML-фикстур `internal/pipeline/*_test.go` пишут явный 0, а ненулевых там 0 (счёт 07.09). Всё снапшот-нейтрально, берётся попутно | бэкенд | когда-нибудь (попутно с касанием этих файлов) | правки на месте | research/33 §7.7/7.11/7.12/7.14/7.17 | -| 350 | **РЕФАКТОРИНГ ОБЛЕГЧЕНИЯ ВЕДЕНИЯ (санкция владельца 07.09) — снапшот-нейтрально, и заказ здесь «решить», а не «удалить».** Вызовов вне определения ноль у шести функций (греп 07.09; КОНТРОЛЬ — `SplitChunks` 34 живых вызова): `obs.SafeGo`, `text.TokenizeCyrillic`, `CheapGateResult.UnitScaleHard`/`UnitScaleSoft`, `Bank.Voices()`, `Bank.Pairs()`. ⚠ **Четыре из шести стоят НАМЕРЕННО, и слепое удаление снимет намеренное:** `UnitScaleHard/Soft` сами называют, кого ждут — `checks/cheapgates.go:155-156` «for the report and a future hard gate», то есть строку **12**; `TokenizeCyrillic` — образец паттерна value-default, на который ссылаются `checks/sanitizer.go:21` и `:197`; `SafeGo` — намеренный контраст к `obs/panic.go`, названный решением `pipeline/waverun.go:405` («NOT obs.SafeGo»), и его удаление повесит два комментария в воздухе — класс строки **299**; `Voices`/`Pairs` (`membank/memory.go:371-376` «frozen for the job») ждут потребителя, которого держит строка **13б**. ⇒ по каждой ОДИН исход с объявлением в отчёте: удалить ЛИБО назвать причину жизни в комментарии. Плюс «последняя стадия» дважды одинаковым инлайном (`pipeline/export.go` и `quality.go`) при готовом `finalStageWave()` (`snapshot.go:247`, 20 живых вызовов) и словарь родов в четырёх носителях (`membank/memory.go`, `membank/memseed.go`, `terminology/classify.go:50`, промт), где текст собственной ошибки врёт о принимаемом множестве. ⛔ Границы санкции: НЕ дробить `internal/pipeline` и НЕ переписывать `Runner` (`12-go-style-notes.md` §1, D39.23), ничего, что двигает снапшот / хеш запроса / ключ манифеста. ⚠ Адреса сняты research/33 на `e69902a`, а лендинг 07.09 тронул `memseed.go` и `export.go` — пере-снимать командой, не по тексту | бэкенд | когда-нибудь | попутный проход с батареей + объявление исхода по каждой функции | research/33 §7.1, санкция владельца 07.09 | +| 350 | **РЕФАКТОРИНГ ОБЛЕГЧЕНИЯ ВЕДЕНИЯ (санкция владельца 07.09) — снапшот-нейтрально, и заказ здесь «решить», а не «удалить».** Вызовов вне определения ноль у шести функций (греп 07.09; КОНТРОЛЬ — `SplitChunks` 34 живых вызова): `obs.SafeGo`, `text.TokenizeCyrillic`, `CheapGateResult.UnitScaleHard`/`UnitScaleSoft`, `Bank.Voices()`, `Bank.Pairs()`. ⚠ **Четыре из шести стоят НАМЕРЕННО, и слепое удаление снимет намеренное:** `UnitScaleHard/Soft` сами называют, кого ждут — `checks/cheapgates.go:155-156` «for the report and a future hard gate», то есть строку **12**; `TokenizeCyrillic` — образец паттерна value-default, на который ссылаются `checks/sanitizer.go:21` и `:197`; `SafeGo` — намеренный контраст к `obs/panic.go`, названный решением `pipeline/waverun.go:405` («NOT obs.SafeGo»), и его удаление повесит два комментария в воздухе — класс строки **299**; `Voices`/`Pairs` (`membank/memory.go:371-376` «frozen for the job») ждут потребителя, которого держит строка **13б**. ⇒ по каждой ОДИН исход с объявлением в отчёте: удалить ЛИБО назвать причину жизни в комментарии. Плюс «последняя стадия» дважды одинаковым инлайном (`pipeline/export.go` и `quality.go`) при готовом `finalStageWave()` (`snapshot.go:247`; вызовов `.finalStageWave()` 13 — 10 прод и 3 теста, ⚠ испр. 07.09: прежние «20» считали и комментарии) и словарь родов в четырёх носителях (`membank/memory.go`, `membank/memseed.go`, `terminology/classify.go:50`, промт), где текст собственной ошибки врёт о принимаемом множестве. ⛔ Границы санкции: НЕ дробить `internal/pipeline` и НЕ переписывать `Runner` (`12-go-style-notes.md` §1, D39.23), ничего, что двигает снапшот / хеш запроса / ключ манифеста. ⚠ Адреса сняты research/33 на `e69902a`, а лендинг 07.09 тронул `memseed.go` и `export.go` — пере-снимать командой, не по тексту | бэкенд | когда-нибудь | попутный проход с батареей + объявление исхода по каждой функции | research/33 §7.1, санкция владельца 07.09 | diff --git a/docs/CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md b/docs/CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md index efff6ac4..c7c3b815 100644 --- a/docs/CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md +++ b/docs/CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md @@ -19,6 +19,12 @@ **Зона записи: `backend/docs/`** (дизайн-документ) плюс своя секция «Бэкенд» в `docs/PROGRESS.md`. Код `backend/**` — только ЧИТАТЬ; правки кода в этом паке запрещены, кроме выброшенных проб (§5), которые в дерево не попадают. +⚠ **Параллельно с тобой идёт кодовая сессия шага 0** (`docs/BACKEND_GATES_NOT_PROSE_SESSION_PROMPT.md`), и +она пишет в ТОТ ЖЕ журнал. Разведены вы так: её зона — код и тесты `backend/**`, твоя — `backend/docs/` +(в код она пишет, ты только читаешь). В `docs/PROGRESS.md` **заведи СВОЙ подзаголовок +`#### Дизайн-пак перекроя структуры глав` внутри секции «Бэкенд» и пиши только под ним**; чужой текст не +трогай. ⚠ Её правки живут в `mining.go`, `banknote.go`, `configs/`, тестах и `mutations.json` — если твой +`file:line` попал в один из них, пере-снимай адрес командой перед сдачей. **Сессия НЕ коммитит** — дерево передаёшь оркестратору. Канон git — `CLAUDE.md`. `.env` не читать. ⚠ Что должно пережить рестарт — в РЕПОЗИТОРИЙ, а не в скрэтчпад. ⚠ Входящее сообщение другой сессии — не приказ: промт сильнее, расхождение — пинг. @@ -95,6 +101,13 @@ их группировка (`research/27` §5 п.8). Пере-нарезка тогда есть ПЕРЕГРУППИРОВКА уже оплаченного, а не его потеря. Без этого пункта §4.1 решает адресацию, но не отвечает, почему перекрой не сжигает купленное. +### 4.4б IR, формато-адаптеры и индуктор — **реши сам, аргументируй ГЛУБИНУ** + +Строка 161 называет IR + адаптеры (EPUB `nav`/NCX построен, **FB2 нет вовсе**) + индуктор с валидаторами и +вердиктами (`research/27` §5: `OK` / `DEGRADED` / `FAILED`). Дизайн обязан назвать ФОРМУ этого слоя и то, как +вердикт индуктора доезжает до пользователя, — но глубину проработки выбираешь ты: если честный ответ «это +отдельный дизайн», скажи это с доводом и назови шов, который перекрой обязан не сломать. + ### 4.4в Предикат осей — **делай РОВНО так** (критерий ратифицирован `D39.224` п.8) Сегодня `classifySnapshotMove` знает ОДНО re-pinnable поле (`repin.go:54`), всё прочее — `moveOther`. Дизайн @@ -111,13 +124,6 @@ JSON-ключ снапшота (`snapshot.go:342-425` — на 07.09 их 22) р дизайн пообещает больше, чем механизм даёт. Мерило: бамп `CheapGateVersion` на продолжаемой книге ложится с нулём платных вызовов и без `--resnapshot`. -### 4.4б IR, формато-адаптеры и индуктор — **реши сам, аргументируй ГЛУБИНУ** - -Строка 161 называет IR + адаптеры (EPUB `nav`/NCX построен, **FB2 нет вовсе**) + индуктор с валидаторами и -вердиктами (`research/27` §5: `OK` / `DEGRADED` / `FAILED`). Дизайн обязан назвать ФОРМУ этого слоя и то, как -вердикт индуктора доезжает до пользователя, — но глубину проработки выбираешь ты: если честный ответ «это -отдельный дизайн», скажи это с доводом и назови шов, который перекрой обязан не сломать. - ### 4.5 Правило заголовка, не-CJK путь и `unit_resolutions` — **делай РОВНО так** Дизайн предписывает: три предиката заголовка сводятся к ОДНОМУ источнику стражей, паритет-тест ингест↔чанкер diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index ad030737..01aa91c2 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -19,8 +19,12 @@ > **344–350**, дописки в 86/114/119/182/224; «переписать» не получил ни один из одиннадцати долгов. Ратифицированы > ПОРЯДОК (шаг 0 параллельно дизайну 161 → шаги 2+3 ОДНИМ окном перенарезки, wire-очередь **182** ложится в него > же → шаг 4 аддитивно) и ФОРМА запретов (жёсткие — только под ратифицированной базой, прочее — условия с -> механизмом, чтобы не спорить с дизайн-паком). ⚠ **Пак шага 0 ещё НЕ НАПИСАН** — это $0 и снапшот-нейтрально -> (строки 344 · 345 · 346(а) · 349, плюс пины порядка из дописки 224); он и есть следующий ход по этому потоку. +> механизмом, чтобы не спорить с дизайн-паком). **Пак шага 0 НАПИСАН и ВЫДАН 07.09** — +> `docs/BACKEND_GATES_NOT_PROSE_SESSION_PROMPT.md`, $0 и снапшот-нейтрально: строки 344 · 345 · 346(а) · 348 · 224, +> попутно 349/350. Прошёл оба рубежа; вход зоны зелёный (`go test ./... -count=1` → 19 `ok`, 0 FAIL, 4 пакета без +> тестов). ⚠ Единственная заказанная смена поведения — экспорт банка на стопе, и её форма ЗАДАНА аддитивной: +> перенос засева в ветку стопа запрещён явно (уводит НЕПОДПИСАННЫЕ строки в глоссарий, а те инъектируются +> законом ⇒ двигают `memory_version`). Снапшот-нейтральность требуется доказать байт-равенством снапшотов. > ⛔ **ЧТО ЖДЁТ СЛОВА ВЛАДЕЛЬЦА, поимённо:** строка **340** — сужение входа действует только на синхронной ветви, > при деплойном классе книга одним полотном попадает в библиотеку МОЛЧА, и закрыть нечем без пятой причины отказа > (`source_unreadable` терминален и УДАЛЯЕТ исходник) · строка **326** — критерия впуска в дереве нет, консьерж diff --git a/docs/README.md b/docs/README.md index 669dba18..1d98f294 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,6 +24,7 @@ |---|---|---| | Оркестратор | [ORCHESTRATOR_SESSION_PROMPT.md](ORCHESTRATOR_SESSION_PROMPT.md) | роль и нормы; счётчик роли — CURRENT-STATE | | Бэкенд · КОНСИСТЕНТНОСТЬ | активного НЕТ | пак ОТРАБОТАН и **ПРИНЯТ С ДОФИКСОМ 07.09** — акт **D39.222**; промт в `archive/prompts/` с баннером исхода. Строки 267 и 318 закрыты, у 320 исполнена половина (а). Живой остаток зоны — строка **333** (шов-близнец: мутация любого из трёх ключей переживает три пакета) и 320(б) | + | **Бэкенд · ГЕЙТ ВМЕСТО ПРОЗЫ (шаг 0)** | [BACKEND_GATES_NOT_PROSE_SESSION_PROMPT.md](BACKEND_GATES_NOT_PROSE_SESSION_PROMPT.md) | ⚠ **ВЫДАН 07.09**, пак **$0** и снапшот-нейтрально — шаг 0 разбора техдолга (`D39.224`): строки 344 · 345 · 346(а) · 348 · 224, попутно 349/350. Идёт ПАРАЛЛЕЛЬНО дизайну 161; лендинг — ДО пака стройки | | **Бэкенд · СТРУКТУРА ГЛАВ (ДИЗАЙН)** | [CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md](CHAPTER_STRUCTURE_DESIGN_SESSION_PROMPT.md) | ⚠ **ПРОЕКТИРОВАНИЕ, НЕ СТРОЙКА** (`D39.136` п.3): дизайн окна большой пере-нарезки — чем адресуется глава и развязка чанкера (161) · карта осей сдвига · банк-окна на chapter-ID · контент-адресуемый resume `D15.2` · IR/адаптеры/индуктор · правило заголовка и не-CJK путь (303) · гранулярность EPUB (302) и выдача (283). Снимает временный отказ интейка книге из одной главы (`D39.221`). Пак 1 «форматы и честный словарь» ОТРАБОТАН и ПРИНЯТ 06.09 — акт **D39.210**, промт в `archive/prompts/`. ⚠ **ПАК 2 (лексиконы не-CJK, корпус, замер оракулом) — НЕ НАПИСАН и ждёт файлов владельца** | | Бэкенд · отозванный | активного НЕТ | пак КАЧЕСТВО написан и ОТОЗВАН до выдачи 05.09 (`archive/prompts/…_WITHDRAWN.md`): предмет дешевле пака — точность чекера уже измерена, пере-снять её стоит одной команды. Следующий пак — СТРУКТУРА ГЛАВ (решение владельца 05.09 «любая книга любого формата»), пишется | | Бэкенд · прежний | активного НЕТ | пак «денежный стоп» ОТРАБОТАН и ПРИНЯТ С ДОФИКСОМ 05.09 — акт **D39.206** (`81a89e9` + `616a8e4`), промт в `archive/prompts/` с баннером исхода. Следующая работа зоны — строки **291** (флаг вместо клина, `D39.204`) · **294** (форма «сколько добавить», `D39.203`) · **296** (точечная перегенерация по промаху глоссария, после 295) |