415 lines
51 KiB
Markdown
415 lines
51 KiB
Markdown
# Пак бэкенда: «ГЕЙТ ВМЕСТО ПРОЗЫ» — шаг 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` ⇒ двигает снапшот обеих волн ⇒ перекупает книгу. Он приземлится в окне перенарезки, чужим
|
||
паком.
|
||
|
||
**Правильная форма — ДВА теста, и поодиночке ни один не годится** (оба в пакете `chunk`: предикаты не
|
||
экспортированы). ⚠ Испр. 07.09 по второму рубежу: первая редакция заказывала ОДИН тест, а один пин
|
||
расхождения — это второе описание бага, а не гарантия.
|
||
|
||
- **(A) ПАРИТЕТ — живая гарантия, зелёная и сегодня, и после фикса.** На НОРМАЛЬНЫХ формах (обычный
|
||
заголовок вида `第三章 正常标题`, номера подряд, длина в пределах стража) предикат ингеста и разбор чанкера
|
||
отвечают ОДНО И ТО ЖЕ, а книга, прогнанная `IngestEncoded` → `SplitChunks`, несёт непустой `Heading` на
|
||
КАЖДОЙ главе. Такого теста в репозитории нет ни одного, и именно он останется жить, когда расхождение
|
||
закроют. Без него (B) ничего не гарантирует.
|
||
- **(B) ПИН РАСХОЖДЕНИЯ — утверждать ВСЕ стороны, иначе частичный фикс пройдёт молча.** Для `第零章:序幕`:
|
||
ингест дал три главы **И** `chunks[0].Heading` пуст **И** текст чанка начинается сырым маркером **И**
|
||
`第一章` рендерится «Глава 1» при `Chapter == 2` (пользовательская половина — она и есть титульный хаос).
|
||
Для 84-рунной строки: страж ингеста говорит «не заголовок», а разбор чанкера — «заголовок, номер N».
|
||
Текст падения обязан объяснять СЛЕДУЮЩЕМУ, что произошло, в форме «расхождение, которое пинует строка
|
||
346, закрылось — сложи этот тест в паритетный».
|
||
|
||
⚠ Красный тест в `main` не ложится (`battery` включает `test`), а скип — ровно класс `D39.208` п.5: «пусть
|
||
пока падает» и `t.Skip` не годятся оба, поэтому (B) утверждает СЕГОДНЯШНЕЕ состояние и зелен.
|
||
⚠ **Строку 346 называй КОММЕНТАРИЕМ и текстом падения, а не именем функции** — конвенция репозитория
|
||
именно такая: ссылок на строки бэклога в именах тестов 0, в комментариях тест-файлов — 55 файлов из 204
|
||
(счёт 07.09).
|
||
|
||
### 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 «что не удалось» · вопросы оркестратору.
|