98 KiB
research/33 — Техдолг бэкенда: замер цены при изменении и что обязан перепроектировать перекрой глав
⟶ СТАТУС: ФАКТУРА, НЕ РАТИФИЦИРОВАНА. Написан контролёром 06.09 по заказу владельца («итог замера техдолга — ресёрч-документом, чтобы по нему принимать решения»). Отдельные утверждения отсюда становятся ратифицированными только собственной D-нотой; статус файла в целом — материал для решения. Строки бэклога по нему заводит оркестратор; сам файл ничего в дереве не меняет и не заказывает.
ЧЕМ СНЯТО. Ветка
main, HEADe69902aна момент письма. Замер шёл воркфлоу из 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 ✔. Батарея на HEADe69902a: 18ok+ 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: носители — строки 344–350 и дописки в 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.go329 ·rebill.go566 ·snapshotdiff.go184 строки ✔, 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.А. Что тормозит РАЗРАБОТКУ сегодня (счёт выставляется сейчас)
- Норма «копить wire-правки и вносить одним касанием» (строка 182 ✔) — единственная измеренная
НАВЕДЁННАЯ инертность. Снаружи текущего батча стоят и названы вслух: 131 (кап инъекции), 105(а)
(вердикт-половина классификатора), 14 (ё-фолд), 119+124+115 (одним дизайн-паком), 160, 142 — часть из
них чистая $0-гейтовая работа. Ждут не из-за риска, а потому что раздельный лендинг стоит денег. Корень
механический:
classifySnapshotMoveзнает ровно одно re-pinnable поле —const memoryVersionField(repin.go:54✔), всё остальное =moveOther= не пере-пиньится ничего (Д5). - Лендинг данных заблокирован порядком.
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). - Порядок предвычисления не пиненный, и один счёт уже не оплачен.
bookrun.go— 0 записей из 211 в собственном каталоге мутаций (то же уseeding.go,snapshot.go,bankexport.go,repin.go) при густом покрытии соседей (bankdecisions.go20–21,volume.go11,waverun.go8–11) ✔. Живое следствие — строки 224/253 ✔:mining.go:269exportBank("bank-mining/signature-stop")стоит СНАРУЖИ веткиif !stopping(:234), а наполняющие банкwriteAutoBank(:241) иseedGlossary(:244) — ВНУТРИ ✔; экран подписи читает пустой артефакт (total 0, signed 0при 82 неразобранных поверхностях — найдено первым живым прогоном 04.09, строка 253 ✔).tmmutateне в батарее:Makefile:58battery: build vet fmt lint test, словаmutateв Makefile нет ✔ (Д9). - Шов заголовка главы не утверждается нигде. Три мутанта переживают всю батарею; причина — вырожденные фикстуры двух тестов, чьи имена обещают ровно эту гарантию (Д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.Б. Что удорожает БУДУЩИЙ перекрой структуры глав (сегодня не платится)
embedded_version— один хеш на 8 файлов, три потребителя, три радиуса взрыва (Д4):snapshot.go:443(провод/вердикт волн) +manifest.go:264и:366(CUT-тег и ключ манифеста) ✔. Правкаtarget-ru.txt($0-словарь вердикта) двигает CUT-тег, хотяTargetChecksForне имеет ни одного call-site вinternal/chunkиinternal/text✔ — то есть физически не может повлиять на нарезку.structure_versionвне снапшота +projectRebillслеп к контентной/позиционной оси (Д6, «дыра Р6»,rebill.go:33-36✔, строка 160 ✔): ревизия структуры через данные перекупила бы хвост МОЛЧА. Порядок величины назван проектом — ~$9–11 на 500 глав за вставку в середину (строка 185 ✔).- Ординал главы несёт три семантики на одном 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— переписывание шины его не покупает. unit_resolutionsплатформы ключеваны позицией (строка 298 ✔) — любой перекрой сносит их целиком; сегодня цена нулевая (решения есть только у стенд-книги), после интейка — нет.- Правило заголовка разъехалось по трём предикатам (длина 60 рун · осмысленность номера
v<=0· обязательность юнита), и ни один не пришпилен ни с одной стороны (Д3). Живой дефект уже сегодня:第0章/第零章/第〇章— ингест главу режет, чанкер заголовок не снимает ⟲, плюс номера главы и заголовка расходятся (第一章становитсяChapter 2, но рендерится «Глава 1» ⟲).
3. Поимённо: одиннадцать переживших долгов
Нумерация — по итогу воркфлоу. Для каждого: предмет · улика · цена при изменении · вердикт · двигает ли лекарство снапшот / хеш запроса / ключ манифеста / версию встроенных данных (= означает ли перепокупку уже оплаченных книг) · носитель. Д10 и Д11 — один шов, найденный двумя срезами (форма тестов и швы), разбираются вместе.
Д1. Закрытые словари классификатора живут в Go и словами в промте пары; для НОВОЙ пары гейта нет
- Улика ✔:
internal/terminology/classify.go:18Types = {name, place, title, term},:50Genders,:26CandidateTypes(+nickname); третья копия —internal/pipeline/banknote.go:80bankTypeOK; те же слова прозой вprompts/zh-ru/classifier.md. Леджер меток промтов по построению смотрит только zh-ru:promptlabel_test.go:53const 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. Платится один раз, при второй паре, молчаливой потерей
качества (тип неверен 12–22 % и
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-407ThinksOnWire— комментарий «Must mirror applyToBody exactly»;models.go:519-535echoMineViolation— ЧЕТВЁРТАЯ версия той же таблицы, и это жёсткий 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:307const chapterHeaderMaxRunes = 60, применён в:323; вchunker.goRuneCountInString— 0 хитов;chunker.go:316if !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.go9 коммитов,chunker.go6, оба сразу 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.07–01.08;target-ru.txt— 6; с 01.08 — ни одного. Деньгами сегодня дёшево (D39.60: «$0.3–0.5 корпуса»; D39.64 «перекупок НЕТ» — потому что живых оплаченных книг тогда не было). Платится другим — порядком лендинга и робостью рефактора (§2.А.2). - Гейта нет, есть дисциплина:
embedded_test.go:12TestEmbeddedVersionMutation✔ утверждает ОБРАТНОЕ желаемому (любой байт любого файла обязан двигать хеш); гасят цену только 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:447StyleCheckVersion: 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_versionre-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».
- 3 теста ✔.
- Цена: после появления
structure.txtправка грамматики двигает границы →ch.Textи плотные номера →msgsContentHashрасходится → платный цикл БЕЗ--resnapshotи БЕЗ сметы согласия. Порядок: ~$9–11 на 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-632manifest 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:151enabled: 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:126PKchunk_status,:38jobs 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+ 1until_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, 5spoilerBlocked, 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.go8, но ни одна не про порядок экспорта (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:138r.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:145chapterChunks[0].Heading = headingText;manifest.go:664if ch.ChunkIdx == 0(восстановление заголовка на read-пути манифеста —tmctl export);export.go:174/:340/:341u.Members[0].Heading;bookbuild.go:526-527подставляет НОМЕР, когдаHeading == "",:536TrimPrefix. Тесты с правильными именами: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в тестах 41–48 в 5–7 файлах (счёт зависит от популяции) — ни одно про не-первый чанк; единственные проверки пустого заголовка — индекс 0 при nil-правиле (chunker_heading_test.go:130) и то самое"" == ""(contractblockers_test.go:316) ⟲. - Вердикт: развязать точечно, ДО дизайн-пака. (а) обеим фикстурам — главу, которая реально режется, и
правило заголовка zh-ru; (б) утверждение «не-первый чанк главы несёт пустой
Heading»; (в) M1/M2/M5 вmutations.json. Доказано исполнением: тело утверждения байт-в-байт + другая фикстура → под HEADok, под M1/M2FAIL(агент). Класс назван проектом вчера — 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.structure5 ссылок в 3 файлах,r.cutStructure2,r.bankSrc4 (агент). Денежный порядок держат 15 тестов и голден ⟲.ThinksOnWire/echoMineViolation(Д2) — полдня, ВНЕ окна перекроя, снапшот-нейтрально.- Нулевой канал langpack (Д7) — пятистрочная правка внутри пака второй пары; к главам не относится.
- Словарь классификатора (Д1) — линт в существующем файле, $0.
- Шов заголовка (Д10/Д11) — фикстуры и мутации, боевой код цел.
5. Порядок развязки — что первым, чтобы остальное подешевело
Шаг 0 — часы, $0, снапшот не двигают, ДО дизайна. Иначе дизайн принимается без обратной связи ровно там, где режут:
- Невырожденные фикстуры
chunker_heading_test.go/contractblockers_test.go+ утверждение о пустомHeadingне-первого чанка + M1/M2/M5 вmutations.json(Д10/Д11). - Мутации на порядок предвычисления
bookrun.go/mining.go+tmmutateвmake battery+ закрытие строки 224 (Д9). - Линт словаря классификатора по канонической форме промта в
prompt_lint_test.go+bankTypeOK→CandidateTypes(Д1). ThinksOnWire→ делегация вCapability.SuppressesThinking(Д2) — снапшот-нейтрально, в любое окно.- ⚠ По Д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/ (шапка).
- «StatusReport → render.go → golden: триада, через которую проходит каждая фича» — ОСТАВИТЬ. Нога
«golden» ложна:
golden_test.goне упоминаетStatusReport/.Status()(0 хитов), добавление поля вStatusReportголден тронуть не может;renderStatusJSON— голыйjson.Encoder, новое поле на машинном пути стоит ноль; платформа читаетstatus --jsonпо аллоулисту на 11 полей. D39.181 ратифицировал класс «новое поле контракта шва». Гейт стоит там, куда перекрой садится:TestEveryCutInputMovesTheTag. - «История уже оценила перекрой: 13 файлов, 5 пакетов, 0 миграций» — не долг. Правильный ответ владельцу
(дизайн-пак), но цены не называет: вторая улика (
0e69bc1) раздута ~4.5×; «ноль миграций» — потому что оба коммита ординал не двигали. Настоящая цена — бампmanifestVersion(manifest.go:709-711), и она уже погашена нормой:TestTheManifestDocumentVersionDoesNotMoveForAnAdditiveField. - «Две идентичности главы: 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✔). Проба перепутала, где дорого: сдвиг границ = вся книга, перенумерация = хвост. - «internal/pipeline — 17 тыс. строк, 31 % имён протекает между файлами» — ОСТАВИТЬ. 31 % — МЕДИАНА репо
(store 47 %, miner 38 %, config 31 %); метрика меряет «это Go-пакет». Перекрой 06.09 (
2f65d1dи соседи) принёс вinternal/pipeline138 строк в 6 файлах против 1197 вchunkи 261 вlang— семантика главы ЛОКАЛИЗОВАЛАСЬ в пакеты, созданные сплитом D39.23.LoadPipeline353 стр. — другой пакет (config). Остаток: время прогона каталога мутаций (126 записей × 71 с) — строка 313, D39.214. - «Стабильный 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). - «Чанкер принимает решения четырёх слоёв в одной функции» — ОСТАВИТЬ. Композиция, не клубок: три из
четырёх решений за своими швами (
stripHeading,assignEditUnits,chapterDraftChunks), инлайном — две строки нумерации. Перекрой 06.09 взял с функции ~10 механических строк. Остаток → Д3 (паритет нуля) и «два числа главы» → §4. - «status.go: 1183 строки, read-model и деструктивная команда на одном получателе» — ОСТАВИТЬ. Перекрой
2f65d1dтронулstatus.goодной строкой в 717 строках отResetChunkStages; дизайн-док перекрояstatus.goне называет; редрайв под тремя мутациями падает четырьмя гейт-тестами (D17 п.5(в)(г) исполнены). Норматив против разреза по размеру. Остаток (доки):backend/README.mdназываетstatus.goread-only проекцией — ложь, там единственный деструктивный путь;status_test.go:376обещает замок, который в одиночку не держит (с убитым гардом тест проходит, потому что первым отказывает rebill-гейт). - «Новая ЦЕЛЬ требует правки Go (
//go:embed) и пере-снапшотит все пары» — ОСТАВИТЬ. Механизм точен, цена не платится: гейт ловит громко (TestGoldenDeterminismкрасен ровно один из 21 пакета), событие не происходило ни разу (4 правки строкиembed, все до 01.08 ✔), забуковано строкой 115 с триггером, соседняя форма разобрана владельцем в D39.62 Q2/Q3. Условный фолд измеренно снапшот-нейтрален (embed-v1-3df4329e3b9eбайт-в-байт). Брать одним касанием со строкой 120 при второй цели. → влилось в Д4. - «Ингест не режет по безъюнитной грамматике» — ОСТАВИТЬ. Недостижимо данными (0
structure.txtна 44 559 файлов); закрыта оговорка строки 303 — три живые книги владельца через настоящийIngestEncoded: EPUB 32/71 глав (declared), плоский en-txt — 1 глава, и заявленная правка на нём НИЧЕГО не меняет (заголовки словесными числительными в блоке оглавления, в теле их нет). Правка 24 строки, батарея зелёная, но делать СЕЙЧАС хуже, чем потом: ветка без корпуса для замера точности. ⚠ Агент честно сообщил заражение своей копии от соседней сессии (общий scratchpad) — перемерено в приватном каталоге. - «Алфавит майнера зашит в
unicode.Han» — ОСТАВИТЬ. Ратифицированная незастроенная фича с номером (D39.50 п.8, строка 35): нужен ВТОРОЙ детектор, не правка этого; молчание не тихое (пак без данных падает громко,TestEmissionFunnelIsReported). Развязка измерена: 50 строк, зелено,Pack.Version()не движется — но покупает ноль (en по-прежнему 0). Второй хардкод —allCyrillicвminer_palladius.go:128. ⚠TestMinerFullBookParityна этой машине скипается (нет jieba-артефакта). - «„Чем кончился прогон“ решается дважды: exit-код и
finished.outcome» — ОСТАВИТЬ. Полеfinished.outcome— write-only во всём репозитории: платформа кадрfinishedвыбрасывает намеренно (sink.go:195), статус берёт из exit-кода; несущая половина загейчена (мутация exit-кода валит 3 теста + голденexit=2); источник ОДИН —res.Flagged. D39.181 §1 п.2 рассматривал биективность и отверг. Остаток: дешёвая допискаtracedCtx()во флаг-фикстуре. - «Пять fold-тестов стерегут функцию, которой в проде нет» — снято, форма «развязать точечно». Волновое
разделение защищено
snapshot_wave_test.go(пропущен грепом пробы): три мутации — 23, 1 и 6 падений. И инверсия:TestBanknoteSnapshotFold— ЕДИНСТВЕННЫЙ сторож мутацииTokenBudgetв общемbuildSnapshotID; удалить «мёртвые» тесты = снять защиту от тихой перепокупки. Остаток:snapshot.go:206-207/:218— протухшие клаузы про последовательный драйвер (драйвер один,bookrun.go:242). - «Пять pipeline-конфигов дублируют настройки; арм-шапки лгут» — снято, «развязать точечно». Механизм
наследования ЕСТЬ и ратифицирован (D39.23 п.(г),
configs/pairs/<пара>.yaml);segmentation— 0 раз в пяти конфигах, живёт в одном пар-файле; эмпирика опирается на пре-регистрированные книго-локальные армы, не на репо-армы. Остаток — живая мина с деньгами: шапки трёх армов «ЕДИНСТВЕННОЕ изменение» ложны на 11–13 ключей;STACK.md:12«флип = один арм-конфиг» молча снимает банкноту + терминолога + классификатор;bank_contour_test.goгрузит только c1. Лечение — минуты. - «
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»). - «Денежный контент-хеш снапшота без рефлексивного стража» — ОСТАВИТЬ. Посадки: удаление
embedded_versionиз payload →TestGoldenDeterminismкрасен (56 из 65 json-тегов пинятся голденом); второй рубеж —ContentHashна резюме (rebillsource_test.go:18-22). Страж в 45 строк пишется без правки Go (payload уже возвращается строкой). Реально не прикрыты два ЛАТЕНТНЫХ слота —langpack_version(строка 41) иescalate_provider_*(строка 229). - «Словарь событий движок↔платформа разъехался на 12 полей» — ОСТАВИТЬ. 12 = три аддитивных добавления
(
volume8,money3,shortfall); «только у платформы» — 0 из 24; 11 из 12 висят на кадре, который платформа выбрасывает намеренно; 12-е — сознательное не-зеркало с нотой (D39.203). Гейт есть:TestANewFieldInsideAKnownEventIsIgnored.chapters_total/units_total— поля МАНИФЕСТА, а он гейчен жёстко;unit_doneпокрыт эпохойstructure_version/shape_epoch(D39.165). - «Конвертер цен написан руками,
CacheWritePerMвыпадает бесшумно» — ОСТАВИТЬ. «День платной записи в кэш» исполнен на нетронутом коде: 3.75 дошло до леджера,CostUSDиEstimateUSDсчитают. Строгий декодKnownFields(true)ловит опечатку; ноль — вендор-факт (D39.137). Мутационный пробел на строке, которую никто не правит; закрывается одной фикстурой с ненулевым значением (~28 фикстур пишут явный 0). - «Раскладка проекта собрана заново в 6 из 9 setup-хелперов и 13 файлах inline» — ОСТАВИТЬ. В YAML
структуры глав НЕТ (0 ключей из 31); перекрой ведётся компилятором (переименование поля — 11 ошибок с
адресами); худший случай (новый обязательный ключ, 407 падений) чинится одним
sedза 0.013 с; риск перепокупки держитBriefHash, не фикстуры. Консолидация размыла бы мультиязычное разнообразие фикстур (D39.208). - «Остаточная жёсткая письменность: 29 строк в 10 файлах» — ОСТАВИТЬ. Хвост ратифицированной карты
D39.60 (149 сайтов / 45 файлов), носители — строки 79/35/115; три члена дизъюнкции в
chunker.go:265мёртвы (IsLetter покрывает); латинный класс уже закрытTargetScriptNonLatin(); до 29 строк дело не доходит — раньше упирается в строку 115. Поправка в пользу пробы: для греческой/арабской цели детектор не нем, а ложно срабатывает — недостижимо, целевых данных одноtarget-ru.txt. - «Два шаблона фолда гейта с противоположной ценой, ничто не заставляет выбрать дешёвый» — ОСТАВИТЬ.
Заставляют четыре носителя: 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. Ни одно из заказанных воспроизведений не
опровергло утверждение; расхождения — только адреса на 1–2 строки, поправлены в тексте.
Взято с агентов замера, не сверял: сквозной прогон Д1 с фейк-провайдером; git log -L по телам правил
заголовка; 300/300 многочанковых глав; шифт-мап и компилятор-пробы §7; всё в §7.1; «1079 строк» проверил, «16
тестов» — нет; «5 боевых call-site TargetChecksFor» — по моему грепу их 3 + определение (в chunk/text
ноль в любом счёте).
Чему доверяю меньше всего:
- Утверждению, что расщепление
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— не классифицированы ни одним агентом). Это дизайн-вопрос, а смета — оценка по чтению. - Моей же поправке к Д5 (вердикт-ось требует переклассификации):
resume.go:24-26сверено, но какие именно стадийные вердикты зависят от каких версий (sanitizer— да;coverage/style_check— по комментариям «observability, never hard gates», то есть, возможно, ни от чего) — по коду не размечал. Направление уверенное, объём — нет. - Частотному доводу Д3 из
git log -L(«тела правил заголовка двигались по 3 раза каждое»): переезд файлов изpipelineвchunk(e33e3be) ломает-L, и агент замера это не оговорил; мой счёт по файлам (9/6/4 коммита) — другая популяция. (Историю армовThinksOnWireмой агент снял по всем 6 ревизиям файла целиком — ей доверяю.) - Числу «~$9–11 на 500 глав» — это оценка самого проекта (строка 185), не замер этого воркфлоу.