Land the backend debt measured as the price of changing it, with twenty claims refuted and none calling for a rewrite

This commit is contained in:
heaven 2026-09-06 16:59:40 +03:00
parent e69902ab70
commit f08325ea59

View file

@ -0,0 +1,739 @@
# 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 % прод-кода ✔.
---
## 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), не замер этого воркфлоу.