textmachine/docs/research/33-backend-debt.md

751 lines
98 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# research/33 — Техдолг бэкенда: замер цены при изменении и что обязан перепроектировать перекрой глав
> **⟶ СТАТУС: ФАКТУРА, НЕ РАТИФИЦИРОВАНА.** Написан контролёром 06.09 по заказу владельца («итог замера
> техдолга — ресёрч-документом, чтобы по нему принимать решения»). Отдельные утверждения отсюда становятся
> ратифицированными только собственной D-нотой; статус файла в целом — материал для решения. Строки бэклога
> по нему заводит оркестратор; сам файл ничего в дереве не меняет и не заказывает.
>
> **ЧЕМ СНЯТО.** Ветка `main`, HEAD `e69902a` на момент письма. Замер шёл воркфлоу из **38 агентов**
> (6 срезов-проб · 31 опровергатель · 1 сводный вердикт; 4.32 млн токенов, 1866 вызовов инструментов) на
> деревьях `d8f723f`/`6457d77`/`8d61663`/`878cac5` — **бэкенд на всех четырёх побайтово равен HEAD**
> (`git diff --stat <c> HEAD -- backend/` пуст, сверено). ⚠ Один опровергатель назвал деревом `535f60a`
> (05.09), где `backend/` отличается на 32 файла (пак структуры глав `2f65d1d`); его адреса перепроверены по
> HEAD и сходятся. Каждую находку отдельный агент пытался **опровергнуть** с установкой «по умолчанию это не
> тормозит»; в документ вошли **11 переживших**, **20 снятых** — с доводом (§7), 8 косметических не оспаривались
> (§7.1). Поверх воркфлоу — моя сверка: адресные грепы по HEAD и **четыре агента-воспроизводителя**, гонявшие
> мутации на копиях дерева с полной батареей (дерево не трогалось).
>
> **ЧТО МЕРИЛИ — цену ПРИ ИЗМЕНЕНИИ, не красоту кода.** Шесть срезов: вторые носители одного знания · цена
> одной правки (сцепление со снапшотом / хешем запроса / ключом манифеста, то есть с перепокупкой) · общность
> движка («заработает ли пара, которой в репо нет, без правки Go») · швы и ответственность · форма тестов как
> долг · инертность по git-истории.
>
> **КАК ЧИТАТЬ МЕТКИ.** ✔ — сверено мной по HEAD (греп/чтение, адрес `file:line` живой) · ⟲ — воспроизведено
> исполнением на копии дерева моим агентом-верификатором 06.09 (мутация + полная батарея
> `go test ./internal/... -count=1`) · **(агент)** — взято из отчёта агента замера, мной не сверено · «вывод» —
> установлено рассуждением, не замером. **Комментарий в коде поведения не доказывает**: источником смысла
> здесь служат тест или прогон, комментарии цитируются как улика того, что проект ЗНАЕТ, а не что код ДЕЛАЕТ.
>
> **ПОПУЛЯЦИИ ЧИСЕЛ** (одно «число тестов» дало у трёх наблюдателей 1157/1246/1252 — три разных популяции):
> 340 `.go` = 138 прод + 202 тест; 46 212 строк прод-кода / 54 489 тестов (1.18); `^func Test` — **1157 в
> `internal/`**, **1246 по всему `backend/`** (с `cmd/`), **1252** с Benchmark/Fuzz ✔. Батарея на HEAD
> `e69902a`: 18 `ok` + 2 без тестов, EXIT 0 ✔. `internal/pipeline` — 17 236 строк / 41 файл = 37 % прод-кода ✔.
> ⚠ **ЭРРАТА 07.09 (оркестратор, `D39.224` п.10): АДРЕСА §3 ЧАСТЬЮ ПРОТУХЛИ.** Замер снят на `e69902a`;
> `git diff --stat e69902a <HEAD> -- backend/` даёт 18 файлов (+1115/71) — лендинг движкового пака 07.09.
> Пере-снято: `export.go:174/340/341` → `:176/342/343` · `terminologist.go:570` → `:603` · `PROGRESS.md:31` →
> `:52`. Контроль в другую сторону: якоря `mining.go` (234/241/244/269) не тронуты, порядок экспорта банка на
> стопе открыт ровно как описан. ⇒ **адрес перед использованием пере-снимать командой, а не читать отсюда.**
> Диспозиции по этому файлу ратифицированы `D39.224`: носители — строки 344350 и дописки в 86/114/119/182/224.
> ⚠ Там же (п.9) сняты ЧЕТЫРЕ утверждения, унаследованные из этого текста: наследование арм-конфигов НЕ
> ратифицировано `D39.23` п.(г) (§7.13) · запрет двигать `classifierVersion` — `D39.195` п.5, не `D39.199` п.5
> (Д1) · ось «обязательность юнита» не в `D39.207`, а в строке 303 (Д3) · триггер Д7 — новая ИСХОДНАЯ пара,
> не строка 115.
> ⚠ **Дописка 07.09, воспроизведением:** §7.1 «последняя стадия двумя инлайнами при готовом `finalStageWave()`» называет НЕ ТУ функцию — та возвращает волну, а дублировалось ИМЯ стадии, и хелпера для него не было. §3 Д3: расхождение длинного заголовка воспроизводится ТОЛЬКО с сепаратором после юнита — без него оба предиката дают `false` на любой длине.
---
## 1. Ответ владельцу одной оценкой
**Бэкенд НЕ техдолговый в том смысле, о котором спрашивает владелец: он не инертный, он недосторожённый.**
Разработку код не тормозит — тормозит одна НОРМА, наведённая денежной моделью (копить wire-правки одним
касанием, §2.А.1). Из одиннадцати долгов, переживших проверку цены, вердикт «переписать» не получил **ни
один**; все одиннадцать — «развязать точечно». Это не мягкость опровергателей: каждый из них доказал
исполнением, что «переписать» здесь дороже развязки, и почти всегда — ровно на ту перепокупку оплаченных
книг, которой владелец просил избежать.
Долг есть, он реальный, и он **одного класса: гарантии держатся прозой, фикстурами и дисциплиной, а не
гейтами**. Шесть из одиннадцати находок (Д1, Д2, Д3, Д9, Д10, Д11) — «шов не сторожится ничем», и три из них
доказаны до артефакта: мутант, убирающий восстановление заголовка из манифеста, оставляет батарею зелёной, а готовая книга
теряет ВСЕ названия глав (Д10/Д11); мутант, сдвигающий заголовок на последний чанк, тоже зелёный; на
нулевой главе `第零章` ингест режет, чанкер заголовок не снимает, и сырой CJK-маркер уезжает модели (Д3).
Это риск КАЧЕСТВА, не риск скорости.
**Второй противовес: долг в основном ЗАРЕГИСТРИРОВАН, а не спрятан.** Бэклог — 230 строк ✔; у шести из
одиннадцати находок есть строка или D-нота (Д2, Д4, Д5, Д6, Д8, Д9); у пяти носителя нет — Д1, Д3, Д7, Д10,
Д11: четыре из них — непокрытый шов, Д7 — недостроенный канал данных; ни одна не архитектурная.
**Здоровые части — поимённо, с уликой** (не вежливость, замерено):
- **Граф импортов — DAG без циклов** (`go list -deps` проходит ✔); `internal/pipeline` — ЛИСТ: его импортируют
только 6 файлов `cmd/tmctl` и `internal/archguard` ✔. Практически: 17-тысячестрочный пакет — чердак, а не
фундамент; перекрой внутри него не ломает ничего снаружи на этапе компиляции.
- **Архитектурные инварианты исполняет машина, не дока:** `internal/archguard` — три go/analysis-анализатора
(`StageSeam`/`RequestSeam`/`EgressSeam`, `archguard.go:79-81` ✔) под `go vet -vettool` из `make battery`;
в шапке честно перечислено, что НЕ ловится, и на каждый класс лежит фикстура без ожидания (агент).
- **Провод пинится побайтово:** `golden_test.go:23-29` ✔ — снапшот, каждый `request_hash`, тела запросов и
итоговые тексты для свежего прогона И резюма; «a changed request_hash = a missed checkpoint =
--resnapshot = the whole book re-billed (D15), which no code-health win can pay for».
- **Денежный контур сделан аккуратно:** `repin.go` 329 · `rebill.go` 566 · `snapshotdiff.go` 184 строки ✔,
16 зелёных тестов (агент); предикат ре-пина консервативен по построению — незнакомое поле считается
различием (`repin.go:61-91` ✔); гейт согласия отказывает ДО единого вызова; стоп называет уехавшую ось.
- **Собственный мутационный каталог** `cmd/tmmutate/mutations.json` — 211 мутаций на 43 файла ✔; проект уже
практикует ту дисциплину, которой его мерили. Дыры ниже — незакрытые углы ВНУТРИ действующей дисциплины.
- **Грамматика главы — данные:** `<root>/<src>/structure.txt` (`lang/structure.go:142` ✔), и дежурный
ревью-вопрос задан ТЕСТОМ, а не намерением: `TestALanguageAbsentFromTheRepositoryCutsFromDataAlone`
(`structure_newlang_test.go:16` ✔; корейский, которого в репо нет, режется одним файлом — ⟲ `markers=["제"]`).
- **Образец правильного разреза плоскостей уже стоит:** `lang/bankdata.go:14-35` ✔ — вторая эмбед-плоскость с
собственным `bankDataAlgoVersion`, вне волнового снапшота, под стражем `TestBankPlaneIsOutsideTheWaveFold`
(`bankdata_test.go:13` ✔). Лечение Д4 — повторить этот разрез, а не изобретать.
- **Миграции** — один append-only список, версия = длина списка (агент); идемпотентные шаги.
- **По оси общности переписывать нечего:** в прод-Go 0 захардкоженных слагов моделей ✔, 0 маркеров
TODO/FIXME/HACK ✔ (единственный хит — слово «hack» в комментарии `quality.go:50`), ветвление по языку в
боевом коде одно и документированное (`ingest.go:493`, GB18030-проба — агент).
- **История не показывает буксования:** rework-rate 14.1 % с учётом переименований, 7.4 % коммитов-исправлений,
недельная выработка последних двух недель на уровне первых (агент, срез инертности). Раскол год-пакета
(`e33e3be`, 44 файла) уже проведён и вышел с зелёным сьютом — механизм крупных перекроев отработан.
**Единственный вектор, за которым стоит следить как за здоровьем:** `Runner` — 16 полей на 25.07 (`e33e3be`)
→ 25 на 31.07 (`8b1d8d1`) → **39 на 06.09** ✔ (агент замера насчитал 18 на 25.07 — другой коммит того дня;
направление то же), 178 методов в 29 файлах ✔, файл трогали 49 коммитов ✔. Но замеренная цена этого роста
сегодня ровно одна и узкая — непиненный порядок предвычисления (Д9), и лечится она часами, не паком.
---
## 2. Два разных списка
### 2.А. Что тормозит РАЗРАБОТКУ сегодня (счёт выставляется сейчас)
1. **Норма «копить wire-правки и вносить одним касанием» (строка 182 ✔) — единственная измеренная
НАВЕДЁННАЯ инертность.** Снаружи текущего батча стоят и названы вслух: 131 (кап инъекции), 105(а)
(вердикт-половина классификатора), 14 (ё-фолд), 119+124+115 (одним дизайн-паком), 160, 142 — часть из
них чистая $0-гейтовая работа. Ждут не из-за риска, а потому что раздельный лендинг стоит денег. Корень
механический: `classifySnapshotMove` знает ровно одно re-pinnable поле — `const memoryVersionField`
(`repin.go:54` ✔), всё остальное = `moveOther` = не пере-пиньится ничего (Д5).
2. **Лендинг данных заблокирован порядком.** `docs/PROGRESS.md:31` ✔ — стоячий запрет класть правку
`internal/lang/data/*.txt`, пока платная книга живая. И прямо в дереве: `lang/structure.go:108-110` ✔ —
правка `cjk-section.txt` «would move EmbeddedVersion and re-cut every book alive **for a refactor that
changes no behaviour**»; `DefaultCJKStructure` пришлось ВЫВОДИТЬ из файла, а не переписать, именно чтобы
это обойти. Поведенчески-нейтральный рефактор в `internal/lang` физически дороже, чем должен быть (Д4).
3. **Порядок предвычисления не пиненный, и один счёт уже не оплачен.** `bookrun.go` — 0 записей из 211 в
собственном каталоге мутаций (то же у `seeding.go`, `snapshot.go`, `bankexport.go`, `repin.go`) при густом
покрытии соседей (`bankdecisions.go` 2021, `volume.go` 11, `waverun.go` 811) ✔. Живое следствие — строки
224/253 ✔: `mining.go:269` `exportBank("bank-mining/signature-stop")` стоит СНАРУЖИ ветки `if !stopping`
(`:234`), а наполняющие банк `writeAutoBank` (`:241`) и `seedGlossary` (`:244`) — ВНУТРИ ✔; экран подписи
читает пустой артефакт (`total 0, signed 0` при 82 неразобранных поверхностях — найдено первым живым
прогоном 04.09, строка 253 ✔). `tmmutate` не в батарее: `Makefile:58` `battery: build vet fmt lint test`,
слова `mutate` в Makefile нет ✔ (Д9).
4. **Шов заголовка главы не утверждается нигде.** Три мутанта переживают всю батарею; причина —
вырожденные фикстуры двух тестов, чьи имена обещают ровно эту гарантию (Д10/Д11).
**НЕ тормозит — проверено историей, а не ощущением:** «правка в четырёх местах при каждой смене стека»
(`ThinksOnWire`, Д2) — стек моделей менялся, смены ушли в `configs/models.yaml`
(`capabilities.reasoning.control`), а блок армов `applyToBody` побайтово одинаков во всех 6 коммитах файла с
04.07 ⟲. Закрытые словари классификатора (Д1) — на zh-ru-работе цена при изменении ноль. Нулевой канал langpack (Д7) — ни разу не оплачен: 0 файлов
`manifest.txt` в `configs/` ✔, каталог пары один (`configs/langpacks` = `ru`, `zh`, `zh-ru` ✔).
### 2.Б. Что удорожает БУДУЩИЙ перекрой структуры глав (сегодня не платится)
1. **`embedded_version` — один хеш на 8 файлов, три потребителя, три радиуса взрыва** (Д4): `snapshot.go:443`
(провод/вердикт волн) + `manifest.go:264` и `:366` (CUT-тег и ключ манифеста) ✔. Правка `target-ru.txt`
($0-словарь вердикта) двигает CUT-тег, хотя `TargetChecksFor` не имеет ни одного call-site в
`internal/chunk` и `internal/text` ✔ — то есть физически не может повлиять на нарезку.
2. **`structure_version` вне снапшота + `projectRebill` слеп к контентной/позиционной оси** (Д6, «дыра Р6»,
`rebill.go:33-36` ✔, строка 160 ✔): ревизия структуры через данные перекупила бы хвост МОЛЧА. Порядок
величины назван проектом — ~$911 на 500 глав за вставку в середину (строка 185 ✔).
3. **Ординал главы несёт три семантики на одном int** (Д8): позиция — PK `chunk_status` (`migrate.go:126` ✔);
время — `spoilerBlocked` (`memory.go:678-686` ✔); граница — `chapterNo++` (`chunker.go:140` ✔). Радиус
замерен: **183 не-тестовых площадки** — membank 70 · store 47 · pipeline 43 · miner 7 · seed 6 ·
terminology 5 · chunk 3 · runevents 1 · obs 1 ✔. **64 % (117/183) лежит вне `internal/pipeline`**
переписывание шины его не покупает.
4. **`unit_resolutions` платформы ключеваны позицией** (строка 298 ✔) — любой перекрой сносит их целиком;
сегодня цена нулевая (решения есть только у стенд-книги), после интейка — нет.
5. **Правило заголовка разъехалось по трём предикатам** (длина 60 рун · осмысленность номера `v<=0` ·
обязательность юнита), и ни один не пришпилен ни с одной стороны (Д3). Живой дефект уже сегодня:
`第0章`/`第零章`/`第〇章` — ингест главу режет, чанкер заголовок не снимает ⟲, плюс номера главы и заголовка
расходятся (`第一章` становится `Chapter 2`, но рендерится «Глава 1» ⟲).
---
## 3. Поимённо: одиннадцать переживших долгов
Нумерация — по итогу воркфлоу. Для каждого: предмет · улика · цена при изменении · вердикт · **двигает ли
лекарство снапшот / хеш запроса / ключ манифеста / версию встроенных данных** (= означает ли перепокупку уже
оплаченных книг) · носитель. Д10 и Д11 — один шов, найденный двумя срезами (форма тестов и швы), разбираются
вместе.
### Д1. Закрытые словари классификатора живут в Go и словами в промте пары; для НОВОЙ пары гейта нет
- **Улика ✔:** `internal/terminology/classify.go:18` `Types = {name, place, title, term}`, `:50` `Genders`,
`:26` `CandidateTypes` (+`nickname`); третья копия — `internal/pipeline/banknote.go:80` `bankTypeOK`; те же
слова прозой в `prompts/zh-ru/classifier.md`. Леджер меток промтов по построению смотрит только zh-ru:
`promptlabel_test.go:53` `const pair = "zh-ru"`. Off-vocabulary строки ответа — только
`Log.WarnContext` (`terminologist.go:570`), отказа нет. `bankTypeOK` не упомянут ни одним тестом (0 файлов) ✔.
- **Замер ⟲:** переименование классов в zh-ru → красен ровно один тест `TestPromptLabelsPinTheirBytes`
(сообщение — про сдвинувшийся sha, ни слова про словарь), документированный `TM_UPDATE_PROMPT_LABELS=1`
перезаписывает одну строку `testdata/prompt-labels.json` → батарея зелёная. Новая пара
`prompts/ja-ru/classifier.md` с локализованными словами ответа (`人名|地名|称号|用語`) → батарея зелёная **без
единого шага автора** (линт прошёл по 12 файлам, включая новый). Сквозной прогон с фейк-провайдером,
отвечающим чужими словами: работа завершается, чекпойнт классификатора оплачен, `Reclassified=0`, реалия банкуется как `place` — ровно черновая эвристика, ради
снятия которой фаза построена (агент, не воспроизводил).
- **Цена при изменении:** сегодня на zh-ru — 0. Платится один раз, при второй паре, молчаливой потерей
качества (тип неверен 1222 % и `name/place` форсирует транслитерацию — D39.65, агент) плюс мелким счётом
(потолок фазы $1/книга).
- **Вердикт: развязать точечно.** Линт КАНОНИЧЕСКОЙ формы промта (с снятыми комментариями — наивная версия
даёт ложно-зелёное, слова есть в HTML-шапке) в уже существующем `internal/config/prompt_lint_test.go`
(89 строк ✔, сегодня линтует `<TAB>`-плейсхолдеры и сам объявляет целью «ja-ru or en-ru pack authored by
copying the zh-ru one»); прототип ~54 строки, краснеет на подложенном ja-ru, зелен на репо (агент).
Плюс свести `bankTypeOK` к `terminology.CandidateTypes` (одна строка импорта; уже экспортирован).
- **Двигает ли:** нет — только тест. Обратная дорога («переписать»: словарь в данные, байты промта) двигает
`prompt_sha256`/`RequestHash`/`classifierVersion`, а **D39.194 п.9 и D39.199 п.5 ратифицировали
«`classifierVersion` НЕ двигать»** ✔.
- **Носитель:** нет (строки 120/258 несут КЛАСС «ни один тест не отвечает на ревью-вопрос», 204 — платформенная
форма; агент). D39.70 санкционировала книжные примеры в `classifier.md`, про идентификаторы классов — ни слова.
### Д2. `Models.ThinksOnWire` пересказывает `Capability.applyToBody`; копий три, каждая пришпилена к своей рукописной таблице
- **Улика ✔:** `config/models.go:389-407` `ThinksOnWire` — комментарий «Must mirror applyToBody exactly»;
`models.go:519-535` `echoMineViolation` — ЧЕТВЁРТАЯ версия той же таблицы, и это жёсткий fail-fast загрузки
(агент: `models.go:290` зовёт `bad(...)`); `llm/capability.go:223` — арм `if effort == "" || effort == "off"`;
enum-валидатор `models.go:667-671` не пускает чужую строку — новый control нельзя сконфигурировать, не открыв
тот же файл. **Проза гниёт, обе штуки в дереве:** `capability.go:78` «effort "" leaves the provider
default» — опровергается `:223`; `models.go:404` «no switch reaches the wire» — ложно для `ReasoningNone`
с явным уровнем (`capability.go:236-238` пишет `reasoning_effort`).
- **Замер ⟲:** сужение арма до `if effort == "off"` → падает только
`TestCapabilityWireBody/GLM_extra_body_disable:_UNSET_effort_also_disables…` (`capability_test.go:133`);
легальное обновление этого одного ожидания → **полная батарея зелёная при разъехавшемся зеркале**: провод
оставляет thinking на дефолте провайдера, `ThinksOnWire` отвечает «подавляет», предупреждение об эхо-зоне
врёт, и ни один тест не возражает.
- **Цена:** единственный потребитель зеркала — `sourceEchoExposure``logger.Warn` (`runner.go:384-385` ✔,
«loud, not fatal»); врущее зеркало гасит РАННЕЕ предупреждение, перевод не портит. Частота ⟲: файл трогали 6
коммитов, блок `switch c.Reasoning.Control` байт-в-байт одинаков во всех шести с создания 04.07 (`636919c`);
смены стека ушли в `configs/models.yaml` (`capabilities.reasoning.control` в строках 230/240/270/285/320).
Прецедент был: предикат родился неверным 25.07 и чинился 02.08 — нашли паком, не гейтом (D39.91 п.4(б),
агент).
- **Вердикт: развязать точечно, ~40 строк, полдня, вне окна перекроя.** Экспортировать
`func (c Capability) SuppressesThinking(effort string) bool` рядом с `applyToBody`; `ThinksOnWire` и switch
в `echoMineViolation` — в делегацию; 8 строк таблицы `config/capability_test.go` перенести в таблицу `llm`,
чтобы ОДИН кейс утверждал и тело провода, и предикат. Цикла нет: `internal/llm` не импортирует `config`.
- **Двигает ли:** нет. Снапшот фолдит `json.Marshal(r.Models.ResolveCapability(model))`
(`snapshot.go:535` ✔) — добавление МЕТОДА JSON-форму `llm.Capability` не меняет. Переписывание слоя
(смена формы) двинуло бы снапшот каждой стадии.
- **Носитель:** строка 114 ✔ (протухший комментарий висит с 02.08); зеркало как дизайн не ратифицировано.
### Д3. Правило заголовка разъехалось по трём предикатам; третий («第0章») ломает пролог; ни один не сторожится
- **Улика ✔:** `ingest.go:307` `const chapterHeaderMaxRunes = 60`, применён в `:323`; в `chunker.go`
`RuneCountInString` — 0 хитов; `chunker.go:316` `if !any || v <= 0 { return 0, false }`
`parseSectionNumeral` отвергает нулевой номер, который регекс ингеста принимает.
- **Замер ⟲:** `第零章:序幕 / 第一章 起始 / 第二章 继续` через боевой `IngestEncoded` → 3 главы; `SplitChunks`
даёт `Chapter=1 Heading="" Text="第零章:序幕\n…"` — сырой маркер уезжает модели; то же для `第0章`;
`parseSectionNumeral("零"/"0"/"") = 0,false`. Побочно: **номера расходятся**`第一章` = `Chapter 2` /
«Глава 1», `第二章` = `Chapter 3` / «Глава 2». Мутация А (страж длины В чанкер) → батарея зелёная; мутация Б
(снять страж ИЗ ингеста) → зелёная. Живое расхождение на HEAD: строка-заголовок в 85 рун — ингест `false`,
чанкер `n=3 ok=true`. Перекрёстного теста «оба видят одно» нет ни в одном из 97 тестов `internal/chunk` ✔⟲.
Здоровая половина сужает предмет: классы числительных и content/separator сведены (агент: 0 расхождений на
194 528 рунах), и заявленная длинная ось на четырёх боевых книгах стоит ноль (агент: 250 778 строк,
guzhenren 2283/2283).
- **Цена:** сегодня на оплаченной книге — 0; на веб-новелле с прологом «第零章» — титульный хаос, ради снятия
которого построена политика пак-13. Частота правок: `ingest.go` 9 коммитов, `chunker.go` 6, оба сразу 4 ✔ —
платится ~раз в месяц и дёшево (последний акт `2f65d1d` — сведение ДАННЫХ, не повторный вывод правила).
- **Вердикт: развязать точечно** — ОДИН перекрёстный тест на реальных формах и сведение двух из трёх
предикатов (длина, `v<=0`) к ингестовским стражам; ось обязательности юнита не трогать (D39.207 → вход в
пак 2, строка 303 ✔). «Переписать» не покупает ничего: research/27 §6 п.2 ✔ — новый детектор = бамп
`chunkerVersion` = обе волны на 100 %; там же §6 «что переиспользуется» ✔ оставляет `matchHeaderLine +
parseSectionNumeral` живыми в роли готовой грамматики.
- **Двигает ли: ⚠ ДА — чанкерная половина.** Идентичность главы не движется (chapter-id считается по
ингестированному тексту ДО `stripHeading`, `manifest.go:209`, агент), но снятие заголовка меняет ТЕКСТ чанка,
который уходит на провод, а `chunkerVersion` фолдится в снапшот (`snapshot.go:344/428` ✔; собственная дока
константы `render.go:55-58` ✔: изменение того, где начинается глава, «is a deliberate re-translation and
must not look current»). ⇒ по дисциплине проекта фикс — это бамп `chunkerVersion` → снапшот обеих волн →
громкий `--resnapshot`. **Сводный вердикт воркфлоу поставил этот фикс в шаг 0 как $0 — я с этим не согласен**
(§5): в шаг 0 идёт только ЗНАНИЕ и заготовка теста, сам фикс — в окно перенарезки, где `chunkerVersion`
бампается всё равно. Единственный законный обход — ратифицировать, что бамп при байт-идентичности оплаченных
книг не нужен; это решение владельца/оркестратора, не сессии (D39.183: заказанность решает заказ).
- **Носитель:** только ось юнита (строка 303, D39.207). Длина и нулевой номер — нигде: `isChapterHeader`
не встречается ни в одном живом доке, `matchHeaderLine` — в двух (строки 160/303 и research/27) ✔⟲.
### Д4. `embedded_version` — один хеш на 8 файлов с тремя радиусами взрыва; правка $0-словаря двигает CUT-тег
- **Улика ✔:** `lang/embedded.go:22` — явный `//go:embed` на 8 файлов; `:25-27` — бамп `embedAlgoVersion`
только при смене набора/порядка. Потребителей три: `snapshot.go:443` (безусловный фолд, D39.64 П0) +
`manifest.go:264` (`cutInputs`) + `:366` (ключ манифеста). `TargetChecksFor` — call-site в
`bankmaterialize.go:307`, `runner.go:402`, `sanitizer.go:201`; в `internal/chunk`/`internal/text` — 0 ✔.
То есть `target-ru.txt` не может повлиять на нарезку, но двигает `cutTag` → ключ манифеста → на платформе
`delete from unit_resolutions where book_id` (строка 198 ✔, `PROGRESS.md:31` ✔). Хеш воспроизведён
независимо и совпал с голденом `capture.golden:3` (агент). `lang/structure.go:108-110` ✔ — проект уже
ЗАПИСАЛ, что поведенчески-нейтральный рефактор здесь стоит перенарезки всех книг.
- **Частота ✔:** 8 коммитов в `internal/lang/data/` за жизнь репо, все 24.0701.08; `target-ru.txt` — 6;
с 01.08 — ни одного. Деньгами сегодня дёшево (D39.60: «$0.30.5 корпуса»; D39.64 «перекупок НЕТ» — потому
что живых оплаченных книг тогда не было). Платится другим — порядком лендинга и робостью рефактора (§2.А.2).
- **Гейта нет, есть дисциплина:** `embedded_test.go:12` `TestEmbeddedVersionMutation` ✔ утверждает ОБРАТНОЕ
желаемому (любой байт любого файла обязан двигать хеш); гасят цену только D39.62 Q3 ✔ («расщепление хеша
пар-данных — НЕ делаем; дисциплина взамен: правки данных копятся в тот же resnapshot-батч») и ручной
запрет `PROGRESS.md:31`.
- **Вердикт: развязать точечно** — довести до ТРЁХ радиусов (нарезка / провод / вердикт) по образцу
`bankdata.go:14-35` ✔. Честная цена: 24 строки + три call-site + переписывание `TestEmbeddedVersionMutation`
(обслуживание по D39.183 — ТОЛЬКО если пак это закажет, и объявляется в отчёте).
- **Двигает ли: ДА — сам ремонт.** Смена набора/схемы хеширования требует бампа `embedAlgoVersion`
(`embedded.go:25-27` ✔) → `EmbeddedVersion` → снапшот + CUT-тег → одна глобальная перенарезка. Ремонт
отдельным актом платит ровно ту цену, которую отменяет ⇒ единственный дешёвый маршрут — ВНУТРЬ уже
ратифицированного единого пере-снапшота (D39.62 Q3, D39.190 п.4 ✔), тем же окном, что перекрой глав.
- **Носитель:** строка 115 ✔ (триггер «первая новая целевая пара» — почти наступил), D39.64, D39.62 Q3.
### Д5. Re-pin предикат знает только банк: правка $0-гейта не пере-пиньится, очередь строки 182
- **Улика ✔:** `repin.go:54` — одно поле; `:61-91` — любое другое расхождение → `moveOther`;
`snapshot.go:447` `StyleCheckVersion: checks.CheapGateVersion` — версия правил, которые по собственному
объявлению «never changes a chunk disposition» (`cheapgates.go:23-27`, агент), фолдится в денежный хеш.
`verdictSnapshotID` — 6 упоминаний, все в комментариях, 0 определений ✔. Консервативность НАМЕРЕННА и
прибита: `TestRepinRefusesAnyMoveThatIsNotBankOnly` (`miningstop_join_test.go:1686` ✔); D15/D20 (агент):
all-or-nothing приемлем для статичной книги Ф1.
- **Арифметическая поправка опровергателя (сверена ✔):** `buildSnapshotID` маршалит ОДНУ структуру и берёт
ОДИН sha256 (`snapshot.go:499-503`) — провод и вердикт лежат в одном объекте, перекрой даёт ОДИН новый id и
ОДНУ перекупку. Довод пробы «пак заплатит дважды» — ложен.
- **Цена:** не доллары (потолок $127/книга на 1500 глав, редактор $114 — `PROGRESS.md:1190`, research/30:157
✔; платная книга одна), а **строка 182** ✔ — норма копить, и очередь за ней (§2.А.1). Бампы кучкуются на
разработке гейтов: строка `CheapGateVersion` менялась 25.07/26.07/01.08/01.08, `SanitizerVersion` — последний
01.08; с тех пор ноль ✔.
- **Вердикт: развязать точечно — но НЕ наивной формой.** ⚠ Моя поправка к сводному вердикту воркфлоу
(«превратить константу в множество с четырьмя вердикт-ключами»): `resumeFromChunkStatus` отдаёт
СОХРАНЁННЫЕ `Disposition`/`FlagReason` (`resume.go:24-26` ✔), пере-классификации в этой ветке нет.
Объявить `sanitizer`/`coverage`/`style_check_version`/`postcheck_gate`/`classifier_version` re-pinnable без
пере-вынесения вердикта = отдавать за $0 вердикты СТАРОГО алгоритма — ровно «HOLE (b)» спеки D15.2 §6
(агент) и тот класс «тихой пере-оценки на резюме», против которого снапшот построен. Честный дизайн —
**вердикт-ось с $0-переклассификацией сохранённых выходов** (детерминированные гейты пере-гоняются по
тексту чекпойнта, потом ре-пин); это проектирование (строки 49, 124 ось (в) ✔), не правка белого списка.
(Пост-чек глоссария — исключение: он пересчитывается каждый прогон и в `chunk_status` не хранится — снятая
находка §7.14.)
- **Двигает ли:** сам предикат — нет (payload не меняется). Но любое РАСЩЕПЛЕНИЕ полей payload (например
`memory_version` на банк и алгоритм) — смена формы = `moveOther` для всех книг в полёте ⇒ только внутри того
же resnapshot-окна (§7.14).
- **Носитель:** строки 49, 124, 182 ✔; D15, D20; D39.31 сознательно не строил (агент).
### Д6. `structure_version` вне снапшота — зарегистрированная «дыра Р6»; лечение из пробы дороже зарегистрированного
- **Улика ✔:** `Structure: r.structure.Fingerprint()` есть в `cutInputs` (`manifest.go:265`) и в ключе
манифеста (`:367`); в `snapshot.go` слова `structure` — 0 хитов (контроль: соседи `LangpackVersion`,
`EmbeddedVersion` там на месте, `:442-443`); `Fingerprint()` во всём `internal/` — определение + 2 прод-вызова
+ 3 теста ✔. `rebill.go:33-36` сам пишет: «WHAT IS NOT PROJECTED (documented, not silent): the CONTENT
axis». `--resnapshot` сравнивает `job.SnapshotID` (`stagerun.go:53`, агент) — снапшот не двигается.
Латентно: `structure.txt` в дереве 0 файлов (`configs/` ✔; всё дерево 44 559 файлов — агент); грамматика
идёт из `DefaultCJKStructure`, выведенной из `cjk-section.txt`, чьи байты в `EmbeddedVersion` — **сегодня
правка грамматики громкая**. Дыра открывается первым же шагом штатного пути «a source absent from this
repository needs a directory, not a Go change».
- **Цена:** после появления `structure.txt` правка грамматики двигает границы → `ch.Text` и плотные номера →
`msgsContentHash` расходится → платный цикл БЕЗ `--resnapshot` и БЕЗ сметы согласия. Порядок: ~$911 на
500 глав (строка 185 ✔). Разработку не тормозит: обходов вокруг дыры в коде не наросло (6 хитов
`Fingerprint()` всего ✔).
- **Вердикт: развязать точечно** — зарегистрированное лечение (строка 160 ✔, research/27:73 ✔): read-path-
расширение `projectRebill` на контентную/позиционную ось — ПОКАЗЫВАЕТ число, не заставляя платить. Плюс один
явный пункт в дизайн-пак: `structure_version` не фолдится ни в снапшот, ни в `Pack.Version()` (агент) —
решить, видит ли консент-гейт правку грамматики как отдельное событие.
- **⛔ Лечение из пробы («одна строка `Structure` в `buildSnapshotID`») — НЕВЕРНОЕ и дороже:** `repinnable()`
отвечает `true` только на bank-only move (`repin.go:305-318` ✔), быстрый путь резюма требует
`ContentHash` и (`SnapshotID` совпал ИЛИ repinnable) (`stagerun.go:91-92` ✔) ⇒ внеси `Structure` в снапшот —
и правка грамматики протухает ВСЕ строки `chunk_status` книги, включая главы, чей текст не двинулся ни на
байт: адресная перепокупка становится полной.
- **Двигает ли:** read-path — нет.
- **Носитель:** строка 160 (отложена ПОРЯДКОМ — D39.190 п.2 ✔), 161, 185 ✔; D39.136 п.3 ✔ (сначала дизайн-пак).
### Д7. Пар-данные нельзя завезти по частям: манифест требует ≥1 канал, оба канала — китайской формы
- **Улика ✔:** каналов два, закрыты в Go (`langpack.go:108-114`: `source-morphology`, `transliteration`);
`:631-632` `manifest declares no channel — omit the file to ship all channels, or list at least one`;
`validate()` при канале `transliteration` требует ПОИМЁННО пиньинь-категории `palladius/initials, finals,
yw, special_i` + `phonotactics/…` (`:519-527`). Манифест — файл ЯЗЫКА-ИСТОЧНИКА (`<root>/<src>/manifest.txt`,
⟲; проба воркфлоу писала «langpack manifest» — форма та же). Отказ жёсткий: `runner.go:349-352` ✔ — при
`gates.terminology.enabled` и `pack == nil` прогон отказывает (`RefusalBadConfig`); в боевом
`pipeline-c1.yaml:151` `enabled: true` ✔.
- **Замер ⟲:** `ko/manifest.txt` из одного комментария + `ko-ru/heading.txt` → ровно заявленная ошибка;
пустой корень + `ko/structure.txt``LoadSourceStructure` грузится, `markers=["제"]`. Агент воркфлоу: 11
таблиц по ОДНОМУ выдуманному токену — пак грузится (ровно запрещённый воркэраунд).
- **Цена:** ни разу не оплачена (0 манифестов ✔, одна пара ✔). Наступает один раз — на второй паре на c1:
либо фиктивные данные китайской формы, либо прогон без банка терминов (продуктовая цель № 1). **К перекрою
глав отношения не имеет:** несущая половина — разрез — живёт вне канала (`lang.LoadSourceStructure`,
`structure.go:142` ✔) и под тестом; за гейтом остаётся только шаблон `heading.txt`, инертный по построению.
- **Вердикт: развязать точечно** — разрешить нулевое число каналов (`langpack.go:631-633`, ~5 строк) внутри
пака второй пары. ⚠ **Крупнее и точечной правкой не закрывается:** категории каналов зашиты в китайскую
форму — пара, которой нужна СВОЯ транслитерация, выразить её не может. Носителя в бэклоге нет.
- **Двигает ли:** нет. Байты манифеста фолдятся в хеш ТОЛЬКО при наличии файла (`langpack.go:244-253` ✔),
присутствующих манифестов 0 ⇒ `Version()` zh-ru не движется; голден пинит путь БЕЗ langpack (строка 41 ✔).
- **Носитель:** нет; D39.64 П4 ратифицировала ОБРАТНОЕ намерение («источник без пиньинь-морфологии грузится
без Palladius-файлов») — строка `list at least one` не дошла один шаг до собственной цели (агент).
### Д8. Номер главы несёт три семантики на одном int — связка реальна, цена подписи завышена на порядок
- **Улика ✔:** позиция — `migrate.go:126` PK `chunk_status`, `:38` `jobs UNIQUE`; время — `spoilerBlocked`
(`memory.go:678-686`); граница — `chapterNo++` (`chunker.go:140`), sticky-reset (`wave.go:84-86`, агент);
ключ банка — `TermID(src, sense, since, until)` хеширует `strconv.Itoa(since)` (`decisions.go:166-173`).
Радиус — 183 площадки, распределение в §2.Б.3 ✔.
- **Замер:** термин с `since_ch=0` инвариантен к пере-нумерации (агент, `-overlay`-тест). На реальном артефакте
прогона (`books/…/guzhenren-coldrun-a.db.mined-signature.yaml`, только чтение ✔): **142 термина, 128 с
`since_ch: 0`, окно у 14**. В авторских сидах владельца ✔: `guzhenren-seed.yaml` (49 терминов) — 3 ненулевых
`since_ch` + 1 `until_ch`; `-v2.yaml` (58) — 3 + 1. Личная плата владельца — ~8 чисел, не «все подписи».
- **Гейты уже стоят:** `manifestChapterID` = SHA-256 по ингестированному тексту (`manifest.go:197-215`,
агент), `TestManifestChapterIDSurvivesAReCutAndAnEditElsewhere` (`contractblockers_test.go:245` ✔);
`TestEveryCutInputMovesTheTag` (`cuttag_test.go:26` ✔) — рефлексией по структуре, перекрой ГРОМКИЙ;
перенос окон — класс `moveBankOnly` (research/27:84 ✔ «bank-only move — репиннимый класс»).
- **Цена:** сегодня — нет улик, что платится; батарея зелёная, точка применения узкая (3 вызова `Bank.Select`,
5 `spoilerBlocked`, 14 файлов с `SinceCh/UntilCh` — агент). Мультиязычность не задета: ординал языконезависим.
- **Вердикт: развязать точечно** — формула `Chapter.ID` + окна банка на chapter-ID, внутри дизайн-пака 161
(D39.136 п.3 ✔). Переписывать строго дороже: перенос окон при неподвижном крое = `moveBankOnly` = $0-репин;
переписывание двигает сам крой = `moveOther` = полная перепокупка.
- **Двигает ли:** окна банка → `moveBankOnly` ($0 для всего, чего правка не достаёт); развязка чанкера от глав →
крой → `moveOther` ⇒ то же окно перенарезки, что Д4.
- **Носитель:** строки 161, 298 ✔; контракт 14 `StructureVersion` (агент); D39.122 п.2(е) форма `unit.id`.
### Д9. Runner — шина на 39 полей: цена платится не за размер, а за непиненный порядок предвычисления
- **Улика ✔:** `runner.go:42` — 39 полей; 178 методов в 29 файлах; рост 16 → 25 → 39 (§1). Каталог мутаций:
`bookrun.go`/`seeding.go`/`snapshot.go`/`bankexport.go`/`repin.go` — 0 из 211; у `mining.go` 8, но ни одна не
про порядок экспорта (FX23 — «map-before-memory»); `Makefile:58` без `tmmutate`. Порядок в `bookrun.go` ⟲:
`seedGlossary` :174 → `exportBank("run-start/seeded")` :180 → `SplitChunksWithChapters` :182 →
`persistManifest` :194 → `r.bankSrc` :198 → `precomputeSticky` :207 → `planVolume` :219 →
`checkRebillConsent` :229 → `translateBookWaves` :242. `exportBank` читает СТОР, не шину
(`bankexport.go:138` `r.Store.GlossaryForBook` ✔).
- **Замер ⟲:** мутация Б (`seedGlossary` за консент-гейт, вплотную к волнам) → **ровно 15 красных**, среди
них `TestGoldenDeterminism`, `TestRebillProjectionIsPerWave`, `TestSingleSystemEndpointCarriesTheBankOnBothWaves`,
`TestABoundedRunCannotOutrunTheConsentThreshold` — денежный порядок держится, и громко. Мутация А
(`seedGlossary` за `exportBank("run-start/seeded")` и `r.bankSrc`, до `precomputeSticky`) → **батарея
полностью зелёная**, при этом «run-start/seeded» публикует банк ПРЕДЫДУЩЕГО прогона. Порядок пиненный
ровно на денежном шве и голый на шве $0-проекций.
- **Счета уже уплачены дважды:** строка 257 ✔ — тот же класс, найден мутационной пробой, СЕГОДНЯ закрыт пинами
(`volumeslot_test.go:866/:932`, агент: WB60/WB64/FX23 → RED); строки 224/253 ✔ — ОТКРЫТЫ (`mining.go:269`
вне `if !stopping`, §2.А.3), срок «до первого внешнего пользователя».
- **Что заявлено и НЕ подтвердилось:** «25 площадок присваиваний в 13 файлах» — 22 в 11 вне `runner.go`, греп
ловил чужой ресивер `sr.` (агент); «read-path пишет в шину» (`status.go:456`) — натяжка: «pure read» там про
стор, не про мемоизацию (агент). Шина — объявленный композит-корень (D39.23, тело в архиве ✔;
`03-implementation-notes.md:5`), толщина уже была замерена и использована как довод за процессный шов
(D39.81, агент).
- **Вердикт: развязать точечно — часы, не пак.** Записи в `mutations.json` на порядок предвычисления
(`bookrun.go`, `mining.go`); `tmmutate` (или его порядковое подмножество) в `make battery`; закрыть строку 224
(экспорт на стопе — ПОСЛЕ наполнения банка). Это тот же механизм, которым проект закрыл строку 257.
- **Двигает ли:** нет — тесты и содержимое артефакта, не провод.
- **Носитель:** строки 224/253/313 ✔ (313: «каталог доказывает, что мутация существует, а не что она ловится»).
### Д10/Д11. Шов «заголовок главы» не защищён ничем: три мутанта переживают всю батарею, под одним из них книга теряет все названия глав
- **Улика ✔:** `chunker.go:145` `chapterChunks[0].Heading = headingText`; `manifest.go:664` `if ch.ChunkIdx == 0`
(восстановление заголовка на read-пути манифеста — `tmctl export`); `export.go:174/:340/:341`
`u.Members[0].Heading`; `bookbuild.go:526-527` подставляет НОМЕР, когда `Heading == ""`, `:536` `TrimPrefix`.
Тесты с правильными именами: `TestSplitChunksHeadingCarried` (`chunker_heading_test.go:101`) обещает «FIRST
chunk only», негативной проверки нет; `TestManifestChunksReproduceTheCut` (`contractblockers_test.go:294`) —
комментарий `:298` «A long chapter so the cut produces several chunks» ✔.
- **Замер ⟲:** обе фикстуры дают **1 чанк на главу** (`perChapter=map[1:1 2:1]`; в чанкерной 2 непустых
заголовка, в манифестной `nonEmptyHeadings=0` и в разрезе, и в реконструкции — сравнение `Heading` там есть
`"" == ""`; «длинная» глава в 5159 байт даёт 1 чанк / 1 юнит). Мутанты M1 (`if i == 0` — заголовок на первый
чанк КАЖДОГО юнита), M2 (`if false`), M5 (`chapterChunks[len-1]`) и M3 (`export.go:340`, а также все три
места 174/340/341 разом) — **каждый переживает полную батарею, 18 ok / 0 FAIL**. Под M2 выданная книга
(`TranslateBook → Export → BuildBook`) несёт «1», «2» вместо «Глава 1», «Глава 2» (агент; механизм
подтверждён чтением `bookbuild.go:526-527` ✔ — номер подставляется при пустом `Heading`); под M1 «Глава 1»
повторяется в середине главы в артефакте `tmctl export`, который читает платформа (агент). На реальной
книге 300 из 300 глав многочанковы (239×2, 57×3, 4×4) — набор упражняет случай, которого в проде нет (агент; главы резались по маркерам `第N节`, не `\f`-препаратом — оговорка агента).
- **Поправки опровергателя (важны, чтобы не чинить здоровое):** боевой код КОРРЕКТЕН`ChunkIdx`
сбрасывается на 0 в каждой главе, `if ch.ChunkIdx == 0` ≡ «первый чанк главы» по построению, round-trip на
невырожденной фикстуре 0 расхождений из 8; связь глава→чанки→edit-unit ЗАЩИЩЕНА (контрольная мутация
«каждый чанк = свой юнит» убита 9 тестами; 27 тестов дают главе >1 чанка) (агент). Не защищено ровно одно —
РАСПОЛОЖЕНИЕ заголовка; за все 570 разрезов батареи `headingOnNonFirst = 0`, но это свойство кода, не
утверждение теста; упоминаний `Heading` в тестах 4148 в 57 файлах (счёт зависит от популяции) — ни одно
про не-первый чанк; единственные проверки пустого заголовка — индекс 0 при nil-правиле
(`chunker_heading_test.go:130`) и то самое `"" == ""` (`contractblockers_test.go:316`) ⟲.
- **Вердикт: развязать точечно, ДО дизайн-пака.** (а) обеим фикстурам — главу, которая реально режется, и
правило заголовка zh-ru; (б) утверждение «не-первый чанк главы несёт пустой `Heading`»; (в) M1/M2/M5 в
`mutations.json`. Доказано исполнением: тело утверждения байт-в-байт + другая фикстура → под HEAD `ok`, под
M1/M2 `FAIL` (агент). Класс назван проектом вчера — D39.208 п.5 ✔ («фикстура, в которой две различные
величины совпадают, делает весь их класс расхождений НЕВИДИМЫМ»); последний коммит по обоим тест-файлам
`2f65d1d` (06.09) ✔ добавил 134 строки в `mutations.json` и оставил обе фикстуры вырожденными. В каталоге ни
одна правка не содержит `chapterChunks`, `ChunkIdx == 0`, `.Heading`, `Members[0]`, `ApplyHeading` ⟲.
- **Двигает ли:** нет — боевой код не трогается.
- **Носитель:** нет. `chunker.go` в каталоге мутаций отсутствует вовсе ✔.
---
## 4. Большой перекрой глав: дизайн-пак, не переписывание
Довод не вкусовой, а денежный и посчитанный проектом раньше этого замера: research/27 §6 п.2 ✔ — любой новый
детектор границ бампает `chunkerVersion`, двигает ОБА волновых снапшота, `$0`-репин не работает
(`repin.go:61-91`), перекупаются обе волны на 100 %; `golden_test.go:23-29` ✔ пинит провод побайтово ровно
потому, что сдвинутый `request_hash` = вся книга заново. Переписывание покупает ноль и стоит ровно того, чего
владелец просил избежать. Под мандатом D39.216 ✔ («где форма не тянет — переписывать; „дешевле подпереть“
доводом не является») развилка выглядит так:
**Спроектировать ЗАНОВО и отрефакторить без заплат (форма не тянет):**
- **Формула `Chapter.ID` + развязка чанкера от глав** (строка 161). Единственный пункт, который заплатой закрыть
нельзя: 183 площадки, 64 % в membank+store. Половина фундамента стоит и запинена (`manifestChapterID`,
`TestManifestChapterIDSurvivesAReCutAndAnEditElsewhere`). Сюда же — **два числа главы**: плотный `Chapter`
(PK, адрес redrive) и номинал в заголовке (`Heading` рендерит исходное `n`) уже расходятся на неплотной
нумерации и на прологе ⟲ — дизайн обязан назвать, каким числом адресуется глава.
- **Банк-окна `since_ch`/`until_ch` → chapter-ID** (`decisions.go:166-173`, `memory.go:678-686`). Класс переноса
`moveBankOnly` = $0-репин; миграция мелкая (14 из 142 добытых, ~8 чисел в сидах ✔).
- **Предикат осей `classifySnapshotMove` + вердикт-ось с $0-переклассификацией** (Д5). Сегодня — одна строковая
константа; обязан стать явной картой осей (провод / вердикт / банк / контент / позиция) с честным ответом,
что делать на каждой: провод — купить, вердикт — пере-вынести из чекпойнта за $0, банк — ре-пин, контент —
показать смету. От него зависит и очередь 182, и то, будет ли перекрой молчаливым.
- **`projectRebill` — контентная и позиционная оси** (Д6, дыра Р6) — одним куском с предикатом выше: две стороны
вопроса «какая ось уехала».
- **Расщепление `EmbeddedVersion` на три радиуса** (Д4) — по образцу `bankdata.go`; сам ремонт двигает хеш ⇒
только внутри окна перенарезки.
- **`structure_version` как самостоятельная ось** консент-гейта — решить в дизайне; ⛔ НЕ через
`buildSnapshotID` (Д6).
- **Правило заголовка** — три предиката свести к одному источнику стражей и завести паритет-тест ингест↔чанкер
(Д3); ось юнита — по D39.207 в пак 2.
- **`unit_resolutions` платформы** (строка 298) — аддитивно, но в ТОМ ЖЕ дизайне: разрешать `(chapter, unit)` в
идентификатор при приёме события (D39.202, агент).
**Оставить и достроить (переписывать нечего):**
- **`matchHeaderLine` + `parseSectionNumeral`** — research/27 §6 ✔ прямо оставляет их в роли готовой
грамматики / первого паттерн-пака индуктора.
- **`Runner` как композит-корень** — объявленное решение (D39.23, статус «частично»), не недосмотр. Перекрой
трогает в шине микроскопическую поверхность: `r.structure` 5 ссылок в 3 файлах, `r.cutStructure` 2,
`r.bankSrc` 4 (агент). Денежный порядок держат 15 тестов и голден ⟲.
- **`ThinksOnWire`/`echoMineViolation`** (Д2) — полдня, ВНЕ окна перекроя, снапшот-нейтрально.
- **Нулевой канал langpack** (Д7) — пятистрочная правка внутри пака второй пары; к главам не относится.
- **Словарь классификатора** (Д1) — линт в существующем файле, $0.
- **Шов заголовка** (Д10/Д11) — фикстуры и мутации, боевой код цел.
---
## 5. Порядок развязки — что первым, чтобы остальное подешевело
**Шаг 0 — часы, $0, снапшот не двигают, ДО дизайна.** Иначе дизайн принимается без обратной связи ровно там,
где режут:
1. Невырожденные фикстуры `chunker_heading_test.go` / `contractblockers_test.go` + утверждение о пустом
`Heading` не-первого чанка + M1/M2/M5 в `mutations.json` (Д10/Д11).
2. Мутации на порядок предвычисления `bookrun.go`/`mining.go` + `tmmutate` в `make battery` + закрытие строки
224 (Д9).
3. Линт словаря классификатора по канонической форме промта в `prompt_lint_test.go` + `bankTypeOK`
`CandidateTypes` (Д1).
4. `ThinksOnWire` → делегация в `Capability.SuppressesThinking` (Д2) — снапшот-нейтрально, в любое окно.
5.**По Д3 в шаг 0 идёт ЗНАНИЕ, не фикс:** паритет-тест ингест↔чанкер на `第零章`/85-рунной строке сегодня
КРАСНЫЙ, а его зелень требует правки чанкера, которая по дисциплине бампает `chunkerVersion` (§3 Д3). Тест
пишется сейчас, приземляется вместе с фиксом в шаге 3. (Сводный вердикт воркфлоу клал фикс в шаг 0 — здесь
расхождение с ним, довод в Д3.)
**Шаг 1 — первым в дизайне: предикат осей** (`classifySnapshotMove` + `projectRebill` + вердикт-ось с
переклассификацией). Он удешевляет всё остальное двумя способами: снимает норму «копить одним касанием» (то
есть распечатывает очередь 182, включая $0-гейтовые правки) и превращает перекрой из «молчаливая перекупка» в
«названная и согласованная». Вся обвязка вокруг него стоит и покрыта (1079 строк ✔, 16 тестов).
**Шаг 2 — расщепление `EmbeddedVersion` по трём радиусам**ТЕМ ЖЕ окном, что перенарезка: ремонт сам двигает
хеш, отдельным актом он платит цену, которую отменяет (D39.62 Q3, D39.190 п.4).
**Шаг 3 — `Chapter.ID` + развязка чанкера от глав + перенос банк-окон + сведение правила заголовка (с фиксом
`第0章` и стража длины)** — `moveBankOnly` для окон, одна перенарезка для остального.
**Шаг 4 — аддитивно:** контракт 14, `unit_resolutions` платформы, фронтовая половина строки 160.
**Ключевое: шаги 2 и 3 — ОДНА перенарезка. Разнести их = заплатить дважды.** Единственное дешёвое окно —
пока платная книга одна (строка 161: «окно закрывается первой книгой внешнего пользователя»).
---
## 6. Чего делать НЕ надо — где рефакторинг дороже, чем терпеть
- **Не переписывать `Runner` и не дробить `internal/pipeline` дальше «ради здоровья».** Норматив
`12-go-style-notes.md` §1: размер — не критерий сплита; D39.23 сплит по измеренной связности исполнен, остаток
— композит-корень. Перенос `seedGlossary` за консент-гейт валит 15 тестов ⟲ — денежный порядок держится;
64 % радиуса перекроя лежит в membank+store ✔; переписывание шины двигает снапшот-байты.
- **Не вносить `Structure` в `buildSnapshotID`** «одной строкой» — адресная перепокупка становится полной (Д6).
- **Не расширять белый список `classifySnapshotMove` вердикт-ключами как «быстрый выигрыш»** — без
переклассификации это отдача стухших вердиктов за $0 (Д5, `resume.go:24-26`).
- **Не строить детектор границ с нуля** — research/27 §6 п.2: бамп `chunkerVersion` → 100 % обеих волн.
- **Не трогать ось «обязательность юнита» вне пака 2** — D39.207, строка 303.
- **Не менять байты промтов, словарей и `classifierVersion` ради стройности** — D39.194 п.9, D39.199 п.5.
- **Не втаскивать снапшот-нейтральные мелочи (Д1, Д2, Д7) в окно перекроя** — им там не место, они дешевле
отдельно. И симметрично: **не откладывать шаг 0 «до пака»** — он $0 и он условие качества самого дизайна.
- **Не копить wire-правки дальше без шага 1** — копление и есть цена; после предиката осей оно перестанет быть
нужным.
- **Не заводить второго носителя** там, где опровергатель показал один источник: `res.Flagged` кормит exit-код,
`finished.outcome` и `CompletedWithFlags` из одного счётчика (§7.11); словарь событий движок↔платформа
аддитивен по построению (§7.16).
---
## 7. Что снято опровергателями — с доводом
Двадцать находок проб пали на проверке цены. Каждая — с решающим доводом, чтобы следующая смена не искала
заново; побочные РЕАЛЬНЫЕ мелочи, которые опровергатели подняли попутно, помечены «остаток». ⚠ Доводы этого
раздела — из отчётов опровергателей (каждый мерил на копии дерева исполнением); мной сверены только адреса с
меткой ✔. Опровергатели работали на четырёх HEAD одного дня с побайтово равным `backend/` (шапка).
1. **«StatusReport → render.go → golden: триада, через которую проходит каждая фича» — ОСТАВИТЬ.** Нога
«golden» ложна: `golden_test.go` не упоминает `StatusReport`/`.Status()` (0 хитов), добавление поля в
`StatusReport` голден тронуть не может; `renderStatusJSON` — голый `json.Encoder`, новое поле на машинном
пути стоит ноль; платформа читает `status --json` по аллоулисту на 11 полей. D39.181 ратифицировал класс
«новое поле контракта шва». Гейт стоит там, куда перекрой садится: `TestEveryCutInputMovesTheTag`.
2. **«История уже оценила перекрой: 13 файлов, 5 пакетов, 0 миграций» — не долг.** Правильный ответ владельцу
(дизайн-пак), но цены не называет: вторая улика (`0e69bc1`) раздута ~4.5×; «ноль миграций» — потому что оба
коммита ординал не двигали. Настоящая цена — бамп `manifestVersion` (`manifest.go:709-711`), и она уже
погашена нормой: `TestTheManifestDocumentVersionDoesNotMoveForAnAdditiveField`.
3. **«Две идентичности главы: int в БД и контент-хеш в манифесте» — ОСТАВИТЬ.** Шифт-мап пака структуры глав
(`backend/docs/CHAPTER_STRUCTURE_SHIFTMAP.json`): 13 стенд-книг, `same=2355, moved=0`, ключ манифеста
пере-минтился на 13/13 — форма двинулась, ни одна идентичность не переехала, $0. Две оси — ратифицированное
различение (D39.122 п.2(е)), моста между ними нет (`manifestChapterID` только в `manifest.go`), гейт против
молчаливой перепокупки стоит (`TestTheCONSENTGateSeesAnInPlaceSourceEdit`, `rebillsource_test.go:138` ✔).
Проба перепутала, где дорого: сдвиг границ = вся книга, перенумерация = хвост.
4. **«internal/pipeline — 17 тыс. строк, 31 % имён протекает между файлами» — ОСТАВИТЬ.** 31 % — МЕДИАНА репо
(store 47 %, miner 38 %, config 31 %); метрика меряет «это Go-пакет». Перекрой 06.09 (`2f65d1d` и соседи)
принёс в `internal/pipeline` 138 строк в 6 файлах против 1197 в `chunk` и 261 в `lang` — семантика главы
ЛОКАЛИЗОВАЛАСЬ в пакеты, созданные сплитом D39.23. `LoadPipeline` 353 стр. — другой пакет (`config`).
Остаток: время прогона каталога мутаций (126 записей × 71 с) — строка 313, D39.214.
5. **«Стабильный chapter-ID движок не читает» — ОСТАВИТЬ.** Читает ПЛАТФОРМА, для которой он построен
(`platform/internal/ingest/manifest.go:124-127`, `books.ordered_through_chapter_id`, D39.208); внутри
движка ординал — ратифицированная граница (D39.122 п.2(а)). Компилятор-пробы: смена типа даёт 25/10 и 44/14
ошибок, 100 % в одном пакете — подпись здоровой границы. Перевод окон банка на хеш невозможен по смыслу
(`spoilerBlocked` — порядковый предикат; контракт 14 README:343).
6. **«Чанкер принимает решения четырёх слоёв в одной функции» — ОСТАВИТЬ.** Композиция, не клубок: три из
четырёх решений за своими швами (`stripHeading`, `assignEditUnits`, `chapterDraftChunks`), инлайном — две
строки нумерации. Перекрой 06.09 взял с функции ~10 механических строк. Остаток → Д3 (паритет нуля) и
«два числа главы» → §4.
7. **«status.go: 1183 строки, read-model и деструктивная команда на одном получателе» — ОСТАВИТЬ.** Перекрой
`2f65d1d` тронул `status.go` одной строкой в 717 строках от `ResetChunkStages`; дизайн-док перекроя
`status.go` не называет; редрайв под тремя мутациями падает четырьмя гейт-тестами (D17 п.5(в)(г) исполнены).
Норматив против разреза по размеру. **Остаток (доки):** `backend/README.md` называет `status.go` read-only
проекцией — ложь, там единственный деструктивный путь; `status_test.go:376` обещает замок, который в одиночку
не держит (с убитым гардом тест проходит, потому что первым отказывает rebill-гейт).
8. **«Новая ЦЕЛЬ требует правки Go (`//go:embed`) и пере-снапшотит все пары» — ОСТАВИТЬ.** Механизм точен, цена
не платится: гейт ловит громко (`TestGoldenDeterminism` красен ровно один из 21 пакета), событие не
происходило ни разу (4 правки строки `embed`, все до 01.08 ✔), забуковано строкой 115 с триггером, соседняя
форма разобрана владельцем в D39.62 Q2/Q3. Условный фолд измеренно снапшот-нейтрален
(`embed-v1-3df4329e3b9e` байт-в-байт). Брать одним касанием со строкой 120 при второй цели. → влилось в Д4.
9. **«Ингест не режет по безъюнитной грамматике» — ОСТАВИТЬ.** Недостижимо данными (0 `structure.txt` на 44 559
файлов); закрыта оговорка строки 303 — три живые книги владельца через настоящий `IngestEncoded`: EPUB
32/71 глав (declared), плоский en-txt — 1 глава, и заявленная правка на нём НИЧЕГО не меняет (заголовки
словесными числительными в блоке оглавления, в теле их нет). Правка 24 строки, батарея зелёная, но делать
СЕЙЧАС хуже, чем потом: ветка без корпуса для замера точности. ⚠ Агент честно сообщил заражение своей
копии от соседней сессии (общий scratchpad) — перемерено в приватном каталоге.
10. **«Алфавит майнера зашит в `unicode.Han`» — ОСТАВИТЬ.** Ратифицированная незастроенная фича с номером
(D39.50 п.8, строка 35): нужен ВТОРОЙ детектор, не правка этого; молчание не тихое (пак без данных падает
громко, `TestEmissionFunnelIsReported`). Развязка измерена: 50 строк, зелено, `Pack.Version()` не движется —
но покупает ноль (en по-прежнему 0). Второй хардкод — `allCyrillic` в `miner_palladius.go:128`. ⚠
`TestMinerFullBookParity` на этой машине скипается (нет jieba-артефакта).
11. **«„Чем кончился прогон“ решается дважды: exit-код и `finished.outcome`» — ОСТАВИТЬ.** Поле
`finished.outcome` — write-only во всём репозитории: платформа кадр `finished` выбрасывает намеренно
(`sink.go:195`), статус берёт из exit-кода; несущая половина загейчена (мутация exit-кода валит 3 теста +
голден `exit=2`); источник ОДИН — `res.Flagged`. D39.181 §1 п.2 рассматривал биективность и отверг.
Остаток: дешёвая дописка `tracedCtx()` во флаг-фикстуре.
12. **«Пять fold-тестов стерегут функцию, которой в проде нет» — снято, форма «развязать точечно».** Волновое
разделение защищено `snapshot_wave_test.go` (пропущен грепом пробы): три мутации — 23, 1 и 6 падений. И
инверсия: `TestBanknoteSnapshotFold` — ЕДИНСТВЕННЫЙ сторож мутации `TokenBudget` в общем `buildSnapshotID`;
удалить «мёртвые» тесты = снять защиту от тихой перепокупки. Остаток: `snapshot.go:206-207/:218`
— протухшие клаузы про последовательный драйвер (драйвер один, `bookrun.go:242`).
13. **«Пять pipeline-конфигов дублируют настройки; арм-шапки лгут» — снято, «развязать точечно».** Механизм
наследования ЕСТЬ и ратифицирован (D39.23 п.(г), `configs/pairs/<пара>.yaml`); `segmentation` — 0 раз в
пяти конфигах, живёт в одном пар-файле; эмпирика опирается на пре-регистрированные книго-локальные армы, не
на репо-армы. **Остаток — живая мина с деньгами:** шапки трёх армов «ЕДИНСТВЕННОЕ изменение» ложны на
1113 ключей; `STACK.md:12` «флип = один арм-конфиг» молча снимает банкноту + терминолога + классификатор;
`bank_contour_test.go` грузит только c1. Лечение — минуты.
14. **«`memory_version` — смешанная ось, ре-пин отдаёт стухшие вердикты за $0» — ОСТАВИТЬ.** Опровергнуто
исполнением в обе стороны: вердикт пост-чека НИКОГДА не пишется в `chunk_status` (`export.go:250-256`),
живёт в `retrieval_state` и пересчитывается каждый прогон — 0 новых вызовов, $0, вердикт пошёл за новым
алгоритмом. Ось — D15.2 §6 «HOLE (b)», строка 49. Расщепление поля = смена формы payload = `moveOther` для
всех книг в полёте → только в окне 182. Остаток: `snapshotdiff.go:157-159` при движении одного
`memory_version` дописывает причину, которой не проверял («the auto-bank grew»).
15. **«Денежный контент-хеш снапшота без рефлексивного стража» — ОСТАВИТЬ.** Посадки: удаление
`embedded_version` из payload → `TestGoldenDeterminism` красен (56 из 65 json-тегов пинятся голденом);
второй рубеж — `ContentHash` на резюме (`rebillsource_test.go:18-22`). Страж в 45 строк пишется без правки
Go (payload уже возвращается строкой). Реально не прикрыты два ЛАТЕНТНЫХ слота — `langpack_version`
(строка 41) и `escalate_provider_*` (строка 229).
16. **«Словарь событий движок↔платформа разъехался на 12 полей» — ОСТАВИТЬ.** 12 = три аддитивных добавления
(`volume` 8, `money` 3, `shortfall`); «только у платформы» — 0 из 24; 11 из 12 висят на кадре, который
платформа выбрасывает намеренно; 12-е — сознательное не-зеркало с нотой (D39.203). Гейт есть:
`TestANewFieldInsideAKnownEventIsIgnored`. `chapters_total`/`units_total` — поля МАНИФЕСТА, а он гейчен
жёстко; `unit_done` покрыт эпохой `structure_version`/`shape_epoch` (D39.165).
17. **«Конвертер цен написан руками, `CacheWritePerM` выпадает бесшумно» — ОСТАВИТЬ.** «День платной записи в
кэш» исполнен на нетронутом коде: 3.75 дошло до леджера, `CostUSD` и `EstimateUSD` считают. Строгий декод
`KnownFields(true)` ловит опечатку; ноль — вендор-факт (D39.137). Мутационный пробел на строке, которую
никто не правит; закрывается одной фикстурой с ненулевым значением (~28 фикстур пишут явный 0).
18. **«Раскладка проекта собрана заново в 6 из 9 setup-хелперов и 13 файлах inline» — ОСТАВИТЬ.** В YAML
структуры глав НЕТ (0 ключей из 31); перекрой ведётся компилятором (переименование поля — 11 ошибок с
адресами); худший случай (новый обязательный ключ, 407 падений) чинится одним `sed` за 0.013 с; риск
перепокупки держит `BriefHash`, не фикстуры. Консолидация размыла бы мультиязычное разнообразие фикстур
(D39.208).
19. **«Остаточная жёсткая письменность: 29 строк в 10 файлах» — ОСТАВИТЬ.** Хвост ратифицированной карты
D39.60 (149 сайтов / 45 файлов), носители — строки 79/35/115; три члена дизъюнкции в `chunker.go:265`
мёртвы (IsLetter покрывает); латинный класс уже закрыт `TargetScriptNonLatin()`; до 29 строк дело не
доходит — раньше упирается в строку 115. Поправка в пользу пробы: для греческой/арабской цели детектор не
нем, а ложно срабатывает — недостижимо, целевых данных одно `target-ru.txt`.
20. **«Два шаблона фолда гейта с противоположной ценой, ничто не заставляет выбрать дешёвый» — ОСТАВИТЬ.**
Заставляют четыре носителя: D39.60 §6.2 (условный фолд — ратифицированное правило), стиль-норма
`12-go-style-notes.md:7`, два зелёных теста, три рантайм-стопа; 3 из 3 гейтов после появления дешёвой формы
выбрали её или не фолдятся. Линт отличить «опциональную фичу» от «оси у всех» не может — генератор ложных
срабатываний.
### 7.1. Не оспаривались (косметика, 8 находок проб — носителей нет, цена копейки; всё «(агент)»)
- Словарь родов — четыре носителя (`memory.go:946`, `memseed.go:595`, `classify.go:50`, промт), и текст
собственной ошибки `memseed.go:614` уже врёт о принимаемом множестве.
- `zh/numeral.txt` уже эталона `cjk-section.txt` на `〇两兩` — вопрос владельцу: намеренно или недосмотр
(потребитель — канал «числительное + ранг-слово» майнера).
- `unicode.Version` вплетён в 5 версионных констант, 4 — в нерепинуемые поля: апгрейд Go = перепокупка всех
незакрытых книг; stdlib-юникод прибит комментарием, `x/text` — механизмом.
- 37 % не-тестовых строк — комментарии (16 048 против 27 585 кода); множитель цены каждого переименования.
- «Последняя стадия» двумя одинаковыми инлайнами (`export.go:279-282`, `quality.go:340-343`) при готовом
`finalStageWave()`.
- Реестр кодировок — закрытый Go-switch (Shift-JIS/EUC-KR требуют case; отказ громкий); резерв токенов
банкноты выведен для кириллической цели (для кана-цели занижен ~2.1×, строка 40).
- 97 из 200 тест-файлов зовут неэкспортируемые имена — переименование, не форма; в оценку перекроя не идёт.
- 6 из 16 никогда не исполняемых функций — мёртвый код (`SafeGo`, `TokenizeCyrillic`, `UnitScaleHard/Soft`,
`Voices`, `Pairs`); `SafeGo` с докой про паник-рекавери, которого никто не использует.
---
## 8. Чего замер НЕ покрыл — прямо
- **Скорость разработки как таковая.** Метрики цикла в репо нет; срез инертности мерил rework-rate и недельную
выработку по git, а один коммит здесь = один пак целой сессии, так что «файлов на коммит» сцепление НЕ
измеряет (агент оговорил сам). Окно замера — 9 недель.
- **Долларовая цена перекроя на сегодняшнем корпусе.** Есть только потолок $127/книга на 1500 глав
(`PROGRESS.md:1190` ✔) и «100 % обеих волн» из research/27; сметы `projectRebill` по реальной книге под
перекрой никто не снимал.
- **Не-CJK ингест на живой книге** — покрыт частично одним опровергателем (§7.9: EPUB режется по оглавлению,
плоский en-txt — одна глава); словесные числительные и книги без разметки глав в теле — не мерены нигде.
- **Мутационное покрытие `prompts/`** — ноль по построению (2 не-Go цели из 211, обе не в `prompts/`) ✔.
- **`TestMinerFullBookParity`** скипается на этой машине (нет `eval/exp16/data/`): фрозен-паритет майнера с
питоновским эталоном в зелени ни одного агента НЕ участвовал (агент).
- **Платформа, фронт, полигон** — не мерились; платформа фигурирует только как потребитель швов.
- **Замер Д10/Д11 на реальной книге** (300/300 многочанковых) сделан по маркерам `第N节`, а не `\f`-препаратом
пайплайна — распределение может слегка сдвинуться, вывод (даже кратчайшая глава даёт 2 чанка) — нет (агент).
- **Рантайм и производительность** — не в предмете; единственное касание — время прогона каталога мутаций
(строка 313).
- **Сквозные прогоны с фейк-провайдером** (Д1) и `git log -L` по телам правил (Д3) — только у агентов замера,
мной не воспроизведены.
---
## 9. Реестр сверки — что сверено лично, что агентами, чему доверяю меньше всего
**Сверено мной по HEAD `e69902a` (✔):** все `file:line` в §3 без метки «(агент)» перечитаны; счётчики файлов,
строк, тестов (три популяции), `Runner` (поля/методы/рост по `git show`), каталог мутаций (211/43 и нули по
файлам), `Makefile`, 183 площадки ординала с распределением, `mining.go` порядок, 8 коммитов `lang/data` и
история строки `embed`, бампы `CheapGateVersion`/`SanitizerVersion`, `TargetChecksFor`, `verdictSnapshotID`,
`Fingerprint()`, банк-артефакт и сиды (только чтение), строки бэклога 14/31/35/41/49/114/115/124/160/161/164/
182/185/198/224/253/257/298/303/313 и число строк 230, тела D15/D20/D39.23/60/62 Q3/64/81/121/136 п.3/183/
190 п.2,4/194 п.9/199 п.5/207/208 п.5/216, research/27 §6 п.2/п.4/«что переиспользуется»/этап 1, батарея на
HEAD (EXIT 0), DAG без циклов, импортёры `pipeline`, анализаторы archguard (3), TODO-маркеры (0).
**Воспроизведено исполнением моими агентами-верификаторами на копиях дерева (⟲):** Д9 — мутации А (зелено) и Б
(15 красных, поимённо); Д3 — `第零章`/`第0章` сквозь ингест→чанкер, `parseSectionNumeral` на нуле, обе мутации
стража длины (зелено), отсутствие паритет-теста, упоминания в доках; Д7 — ошибка нулевого канала, загрузка
`ko/structure.txt` из пустого корня, `runner.go:349-352`, `pipeline-c1.yaml:151`; Д10/Д11 — M1/M2/M3/M5
(каждый 18 ok / 0 FAIL), измеренная вырожденность обеих фикстур, отсутствие утверждения о не-первом чанке,
отсутствие записей каталога по шву; Д1 — новая ja-ru пара с чужими словами (зелено), переименование в zh-ru
(один красный, штатный рефреш, зелено), 0 тестов на `terminology.Types`/`bankTypeOK`, содержимое
`prompt_lint_test.go`; Д2 — проба 1 (один сабтест), проба 2 (зелено при врущем зеркале), 6 коммитов файла с
байт-идентичным блоком армов, YAML-контролы, строка 114 с 02.08. Ни одно из заказанных воспроизведений не
опровергло утверждение; расхождения — только адреса на 12 строки, поправлены в тексте.
**Взято с агентов замера, не сверял:** сквозной прогон Д1 с фейк-провайдером; `git log -L` по телам правил
заголовка; 300/300 многочанковых глав; шифт-мап и компилятор-пробы §7; всё в §7.1; «1079 строк» проверил, «16
тестов» — нет; «5 боевых call-site `TargetChecksFor`» — по моему грепу их 3 + определение (в `chunk`/`text`
ноль в любом счёте).
**Чему доверяю меньше всего:**
1. Утверждению, что расщепление `EmbeddedVersion` — «24 строки + три call-site» и что образец `bankdata.go`
переносится один-в-один: три радиуса требуют решить, к какому относится каждый из 8 файлов (`injection.txt`
едет на провод, `cjk-section.txt`/`terminator.txt` — резка, `target-ru.txt` — вердикт, `refusal.txt`,
`sentence-abbrev.txt`, `lang-script.txt`, `script-series.txt` — не классифицированы ни одним агентом). Это
дизайн-вопрос, а смета — оценка по чтению.
2. Моей же поправке к Д5 (вердикт-ось требует переклассификации): `resume.go:24-26` сверено, но какие именно
стадийные вердикты зависят от каких версий (`sanitizer` — да; `coverage`/`style_check` — по комментариям
«observability, never hard gates», то есть, возможно, ни от чего) — по коду не размечал. Направление
уверенное, объём — нет.
3. Частотному доводу Д3 из `git log -L` («тела правил заголовка двигались по 3 раза каждое»): переезд файлов
из `pipeline` в `chunk` (`e33e3be`) ломает `-L`, и агент замера это не оговорил; мой счёт по файлам
(9/6/4 коммита) — другая популяция. (Историю армов `ThinksOnWire` мой агент снял по всем 6 ревизиям файла
целиком — ей доверяю.)
4. Числу «~$911 на 500 глав» — это оценка самого проекта (строка 185), не замер этого воркфлоу.