textmachine/docs/archive/reports/GENERALITY_RESEARCH_2026-07-31.md

84 KiB
Raw Blame History

РЕСЁРЧ ОБЩНОСТИ — инвентаризация языковой привязки движка. Фаза 1, отчёт бэкенд-сессии, 2026-07-31

Промт: docs/BACKEND_GENERALITY_RESEARCH_SESSION_PROMPT.md (оркестратор №9, курсовая директива владельца D39.59). Режим: read-only, $0. Кода не тронуто ни строкой — git status по backend/ пуст, ни одного go build/go test/gofmt с правкой не запускалось. Метод: 8 срезов кода просканированы, каждый АДВЕРСАРИАЛЬНО перепроверен вторым агентом (автор≠ревьюер) с прогоном чужих grep-команд; отдельно 5 сквозных разборов (цены снапшота · синк с D-логом · пересборка пака-21 · сухой прогон второй пары · алгоритмы), каждый тоже кросс-чекнут. 26 проверяющих проходов; несущие числа я пере-мерил сам и помечаю это явно.

Ревью-шапка (оркестратор №9, 31.07): фаза 1 ПРИНЯТА, D39.60; фаза 2 — СТОП до пяти ответов владельца (§12). Короткая приёмка по норме 30.07: отчёт прочитан целиком; восемь несущих утверждений пере-проверены исполнением — рецепт §6 воспроизвёл хеш пака моим прогоном (a28ed743c99c), счёт живых CJK-литералов совпал (27 строк / 12 файлов), ko/Hangul-асимметрия подтверждена (cjkShare = Han+кана, Hangul — в 5 сайзинг-сайтах и в 0 вердиктных), ExportNormalize действительно без цель-гейта, embedded.go действительно без хеширования, цитата-заморозка D39.24 в repair.go:182-189 дословна («repayable only … as one deliberate change to the target seam»), segmentation не ставит ни один шиппинг-конфиг, Ш-1 (goldenMaskedDiff) построен. Два нита, не влияющих на выводы: (а) «6 прод-сайтов» ExportNormalize — якорей перечислено 7 (5×waverun + export + quality); (б) «Ш-1 построен вопреки записи в D-логе» — неточность: лендинг D39.17 Ш-1 записал, устарела только плановая записка (строка 604 D-лога). Рекомендации оркестратора по пяти вопросам §12 — в D39.60. ⚠ Контекст, узнанный ПОСЛЕ сдачи этого отчёта: DeepSeek 31.07 сменил веса flash под тем же слагом (D39.61, строка 74 бэклога) — исполнение «общего resnapshot» из §9 гейтится этой развилкой; сама карта от неё не зависит.


1. Эхо-блок

  1. Книго-привязки в движке практически нет: 4 сайта из 118 — и это главный положительный результат по оси, которую владелец назвал «НИКОГДА».
  2. Долг = 149 сайтов в 45 файлах (133 в .go), из них 111 не забуканы; вёдра: A=27 · B=6 · C=104 · D=12 · легитимно E=106.
  3. Ось долга — ИСТОЧНИК/ПАРА 63%, ЦЕЛЬ (ru) 30%, книга 3%. Но у 12 дефектов пака-21 пропорция ОБРАТНАЯ (доминирует цель) — пак-21 чинит не ту ось, о которой директива.
  4. Долг меньше, чем в бэклоге: строка 32 пишет «isRuTarget×11 · TokenizeCyrillic×6» — это счёт грепа; боевых call-site 6 и 2. Живых CJK-литералов в исполняемом Go — 27 строк в 12 файлах из 201 (87% — комментарии).
  5. Вторая пара уже едет: golden-фикстура — ja→ru книга (internal/pipeline/testdata/golden/book.yaml), скелет доказан. Но она идёт через prompt_override, без langpack и без пар-конфига — полный стек второй парой не проверялся никогда.
  6. ja→ru запустится безопасно, en→ru — небезопасно: для en молча спят детектор эха (cjkShare=0) и майнер (hanRuns=0). Требуют Go, не данных.
  7. Цена: любой сдвиг снапшота = перепокупка книги целиком (snapshot.go:51-57); ре-пин возможен ровно у одной оси из 20 (memory_version). Вынос литерала в configs/langpacks/ двигает LangpackVersion включая комментарии; вынос в internal/lang/data/ — $0.
  8. Найдена дыра дисциплины: internal/lang/data/*.txt (6 файлов, из них injection.txt едет НА ПРОВОД, target-ru.txt определяет вердикты санитайзера) не хешируются ничем — «байты ЕСТЬ версия» на эту плоскость не распространяется.
  9. Найден живой баг сверх задачи: isCJKLang числит ko, а cjkShare/CJK-leak/repair-гард Hangul НЕ содержат — корейское (и любое не-Han) эхо проходит молча и не эскалируется.
  10. Момент выбран верно и это дешевле не станет: оплаченный корпус сегодня — два мини-прогона масштаба $0.130.23; один общий --resnapshot стоит центы, после МАСШТАБА — порядки.

2. Что проверено исполнением лично (не агентами)

Заявление Команда Результат
размер кодовой базы find backend -name '*.go' | wc -l + -not -name '*_test.go' 177 файлов / 50 456 строк; прод 83 файла / 27 102 строки
CJK-литералы в проде find backend -name '*.go' -not -name '*_test.go' | xargs grep -nP '[\x{4e00}-\x{9fff}\x{3040}-\x{30ff}]' | grep -vP '^\S+:\d+:\s*(//|\*)' 27 строк в 12 файлах (всего с CJK — 201 строка, 174 = комментарии)
кириллица в проде та же форма с [\x{0400}-\x{04ff}] 66 строк в 16 файлах (всего 421, из них 355 = комментарии)
строка 32 бэклога grep -rn 'isRuTarget' --include='*.go' backend/ 10 строк = 6 call-site + 1 определение + 3 комментария
то же grep -rn 'TokenizeCyrillic' 6 строк = 2 вызова + 1 определение + 3 комментария
эмбед не хеширован grep -n 'embeddedFS|sha256|Sum(' backend/internal/lang/embedded.go только :21 объявление и :469 ReadFileхеширования нет
руби-канал мёртв grep -rn 'rubyToCandidates' --include='*.go' backend/ прод-вызовов ноль (seeding.go:47 зовёт только AttachRubyAliasesToManual)
асимметрия Hangul grep -rn 'Hangul' --include='*.go' backend/ | grep -v _test Hangul ЕСТЬ в 5 сайтах сайзинга/нормализации, НЕТ в 3 сайтах вердикта
matchVersion ре-пинуем grep -n 'matchVersion' backend/internal/membank/memory.go :383 — фолдится ВНУТРЬ memory_version ⇒ путь moveBankOnly открыт
ExportNormalize без гейта grep -rn 'ExportNormalize(' --include='*.go' backend/ | grep -v _test 6 прод-сайтов, ни один не за isRuTarget

3. Карта по пяти вёдрам

Вёдра: A = вынести в пар/цель-ДАННЫЕ · B = пар-скоупнутый Go-модуль за гейтом · C = общий алгоритм + величина/таблица из данных · D = удалить · E = легитимно (не долг).

Полный машинный список якорей — ниже по вёдрам, в форме файл:строки. Каждый сайт открывался и читался телом; заявления, не подтверждённые верификатором, помечены.

3.1 Ведро A — вынести в данные (27 сайтов, 17 файлов)

configs/langpacks/zh-ru/dc-checkers.txt:19   configs/pairs/ja-ru.yaml:2
internal/checks/cheapgates.go:223,250,340,367   internal/checks/output.go:31
internal/checks/repair.go:103                internal/checks/sanitizer.go:465,777
internal/chunk/chunker.go:229,551            internal/chunk/ingest.go:404,410
internal/membank/memory.go:803               internal/miner/miner.go:43
internal/miner/miner_palladius.go:73         internal/miner/miner_patterns.go:24,200
internal/miner/miner_substrate.go:29         internal/text/data/trad2simp.txt:320,544
internal/text/norm.go:147,198                internal/terminology/terminology.go:647
internal/lang/data/cjk-section.txt:28        internal/checks/sanitizer.go:360
internal/checks/cheapgates.go:238

Несущие сайты, по убыванию последствий:

  • internal/text/norm.go:147 + internal/text/data/trad2simp.txt (508 отображений). Таблица традиционный→упрощённый КИТАЙСКИЙ применяется по-рунно к КАЖДОМУ исходному ключу КАЖДОЙ пары, без языкового параметра, из 20 call-site (банкнота, терминолог, банк, майнер). Для ja она активно вредит: содержит 準→准, 鐘→钟, 餘→余, 廣→广, 藝→艺 — обычные современные японские кандзи переписываются в китайские формы и часть различий схлопывается в один ключ; при этом японских синдзитай-фолдов (辺, 沢) в ней НЕТ (grep -c 辺 backend/internal/text/data/trad2simp.txt → 0). Верификатор поправил формулировку: промах 渡邊/渡辺 существует и БЕЗ таблицы (это пробел данных), а вред самой таблицы — в обратном: она сливает ja-различия. grep -n 'trad2simp\|NormalizeSourceKey' backend/internal/text/norm.go
  • internal/checks/cheapgates.go:367translitInterjections (забукана строка 26). Кириллический словник в Go, потребитель — свободная функция lintTranslitInterjections (:374), вызываемая безусловно из RunCheapGates (:153): ни пар-, ни цель-гейта нет вообще. Содержательно список смешанный — «нани/десу/кья» японские, «ауч/упс/хмф» английские (шапка :356 говорит «JP/EN fillers»); формулировку строки 26 «переезд в данные ЦЕЛИ» стоит уточнить, но переклассификация в ja-ru (предложенная одним агентом) опровергнута: сканируется ВЫХОД, привязки к паре в механизме нет.
  • internal/checks/cheapgates.go:223,250 — русская типографика диалога Go-свитчем ('—', '«', '»', ','). В ТОМ ЖЕ пакете уже есть данными-выраженный двойник: voice.go:255,264-297 сегментирует реплики через reply_dash/reply_open/reply_close из internal/lang/data/target-ru.txt:166-168. Два сегментатора речи в одном пакете, один на данных, второй на литералах. Поправка верификатора: данными покрыты 3 литерала из 6 (эн-тире, дефис и запятая атрибуции двойника не имеют).
  • internal/miner/miner_palladius.go:73strings.ReplaceAll(t, "ъ", ""). Русское орфографическое правило в файле, чья шапка (:14-17) декларирует «ТАБЛИЦА — это ДАННЫЕ, тут только АЛГОРИТМ». Путь на прод подтверждён: isPalladiusToken:74PalladiusConformance:117terminologist.go:169.
  • internal/membank/memory.go:803 — формат строки редакторской инъекции («•», « → », «»») собирается в Go, тогда как ВСЕ заголовки и аннотации вокруг уже уехали в internal/lang/data/injection.txt. Ёлочки — целевая типография.
  • internal/checks/repair.go:103,138时辰/半个时辰 Go-литералами в operator-detail, тогда как линт-двойник рендерит ту же строку из пар-данных (checkers.go:272,295dc-checkers.txt:76-77). checkers.go:21 заявляет «this file no longer holds any language literal» и держит это тестом; repair.go (пак-16) дисциплину не унаследовал.
  • internal/lang/data/cjk-section.txt:28chapter_unit = 章 节 節 回, при том что шапка файла называет себя «Universal CJK … A linguistic CONSTANT». (доминирующая единица японских вебновелл, 第1話) отсутствует; detectChapterUnit вернёт 0 и книга станет ОДНОЙ главой молча.

3.2 Ведро B — легитимный пар/семейный модуль, но без гейта (6 сайтов)

internal/lang/langpack.go:772   internal/membank/memseed.go:510
internal/miner/miner_alias.go:93   internal/miner/miner_patterns.go:183
internal/miner/miner_substrate.go:74   internal/miner/miner_palladius.go:115
  • miner_substrate.go:74 — генератор кандидатов «любая N-грамма 1..6 внутри рана письма без сегментации». Это факт о scriptio continua, данными не выражается. Легитимен как код — но пар-гейта нет: mining.go:81 зовёт майнер при любом r.pack != nil.
  • langpack.go:772 (+:486-492) — семь ОБЯЗАТЕЛЬНЫХ категорий Палладия (initials/finals/yw/special_i/retroflex/vfinal/vfinal_initial). Поливанов — плоская мора→кириллица, кросс-произведения инициалей и финалей в нём нет. ja-пак либо не грузится (возвращается ошибка, не паника — поправка верификатора, validate():494), либо проходит валидацию ложью. Самый жёсткий блокер слоя данных.
  • memseed.go:510 — японский классификатор фуриганы («вся база — кандзи, всё чтение — кана ⇒ кандидат-имя», плюс руны / на :546) живёт в общем банке без всякого гейта. Мёртв на практике (см. §5), но класс — пар-модуль в общем слое.
  • miner_patterns.go:183 / miner_alias.go:93 — порядок имени (фамилия = префикс) и контейнмент как признак алиаса. Оба — control flow, не таблица. Для пробельного источника контейнмент даёт ложные рёбра (Ann ⊂ Anna).
  • miner_palladius.go:115 — «слово = максимальный ран букв» (найдено верификатором, сканер пропустил). Для цели без пробелов вся строка станет одним токеном.

3.3 Ведро C — общий алгоритм + величина из данных (104 сайта, 35 файлов)

Самое населённое ведро; ниже — по классам, полный список якорей приведён после.

C-1. Оценка токенов и калибровка нарезки (забуканы 34/40; 11 сайтов). render.go:349-366 EstimateTokens = cjk + other/3, флор 16, где cjk = HanHiraganaKatakanaHangul одним классом. Из неё: max_tokens каждого вызова (stagerun.go:116) → резервация в ledger → request_hash; бюджет банкноты (banknote.go:287); вторая независимая копия той же арифметики в memory.go:494-506 тратит glossary_token_budget. Таксономия классов зашита в Go-типе (config/pipeline.go:136 — ровно два поля, одно названо cjk): пара, которой нужен третий коэффициент, не выражается данными. Литерал bankWorstLineTokens = EstimateTokens("蛊"×8 + "я"×40) (banknote.go:292, забукан 40) — worst-case прямо CJK-src/кириллица-dst. Последний фолбэк калибровки — литералы zh-ru прямо в Go (config/pipeline.go:843-852: 1797/3200/1.1978/0.3852) и configs/pairs/{ja,en}-ru.yaml их наследуют, потому что блок segmentation в них отсутствует. Поправка верификатора: комментарий pipeline.go:832-841 утверждает «every SHIPPING pipeline sets the whole block explicitly» — это неправда, grep -n 'segmentation' backend/configs/pipeline-*.yaml пуст.

C-2. Цель-шов ru (забукана 32; ~18 сайтов). isRuTarget (disposition.go:310) + 6 боевых call-site (chunkrun.go:52, waverun.go:386,500, quality.go:215, repair.go:208,574) · TokenizeCyrillic (text/runes.go:32) + 2 вызова · 12 живых сайтов unicode.Cyrillic · ё→е фолд в общем нормализаторе (text/norm.go:198) · реестр целей Go-мапой (lang/embedded.go:25) · var ruSanitizer = lang.TargetChecksFor("ru") (sanitizer.go:21, пакетная переменная на init). Важная характеристика класса: гейт isRuTarget спроектирован на молчание, а не на ложь (disposition.go:305-309) — на не-ru цели гейты просто не срабатывают. Это долг ПОКРЫТИЯ, не КОРРЕКТНОСТИ. Исключение — checkers.go:393 isCyrLetter: на не-кириллической цели ВСЕ проверки границы слова проходят, и каждая запись registerNeg вырождается в голый подстрочный матч (находка верификатора, класс — ложное срабатывание).

C-3. Экспорт без гейта — проверено лично. checks.ExportNormalize применяется к КАЖДОМУ финалу из 6 прод-сайтов (waverun.go:535,539,541,617,619 · export.go:324 · quality.go:368) без предиката цели, при том что шапка sanitizer.go:17-20 утверждает «the sanitizer is isRuTarget-gated». Внутри — ideographicPunctFold (sanitizer.go:666): ,, ., с мотивацией «a «、»/«。» in a Russian final is an unambiguous wrong-glyph rendering». На CJK-цели это не no-op, а порча. Одна оговорка, которая делает этот пункт дешёвым: ExportNormalize по своей же шапке (:673-677) НЕ является входом снапшота/вердикта — «a change re-projects existing checkpoints for free, no re-bill». Гейт сюда = $0. (Первичное заявление агента про «французский пробел» ОПРОВЕРГНУТО кросс-чеком: spaceBeforePunctRE живёт в StripCosmetic, а тот гейтится. В отчёт вошла только выжившая улика.)

C-4. Детекторы, привязанные к письменности источника. disposition.go:271 cjkShare (Han+кана, БЕЗ Hangul) — единственный детектор эха в интринзик-классификаторе и единственный эскалируемый триггер эха (disposition.go:142-149). Для en он структурно недостижим. Сюда же sanitizer.go:433 (CJK-leak) и pipeline/repair.go:435 (гард ремонта) — те же три письменности. Живой баг: isCJKLang (disposition.go:297-303) числит ko наравне с zh/ja, а Hangul в трёх детекторах отсутствует — то есть для корейского источника движок (а) считает нарезку по CJK-фертильности, (б) печатает предупреждение sourceEchoExposure (runner.go:388), и при этом (в) не может увидеть эхо. Это не вопрос общности, это дефект.

C-5. Сегментация исходника. ingest.go:150,185-214 — детект глав только по 第<цифры><юнит>; ingest.go:266,319 — лестница кодировок с единственной не-UTF-8 ветвью GB-семейства и пар-литералом "zh"; ingest.go:652 — экран кодировки epub видит только XML-декларацию (забукана 34а); chunker.go:605var sourceAbbrevs = lang.SentenceAbbrev("en"), ключ прибит в Go при том, что данные корректно секционированы по источнику. Существенная поправка кросс-чека: отказ «вся книга = одна глава» — только для .txt. ingestEPUB (ingest.go:449-553) кладёт главу на spine-документ и splitTextChapters не зовёт вовсе, поэтому en-EPUB и ja-EPUB с 第N話 нумеруются штатно, без правки Go.

C-6. Пороги майнера и банка. FrozenConfig целиком (miner.go:43-58), длины кандидатов 1..6 / 2..4 / 2..6 (miner_substrate.go:29, miner_patterns.go:188,200), пороги эмиссии emitMinFreq=5/emitRankCap=200/runeLen<2 (miner_emit.go:27,33,306 — последний и есть Go-половина забуканной строки 18: в один рун не проходит ни через эмиссию, ни через алиас-канал — третий сайт того же порога найден верификатором на miner_alias.go:44,93), пороги ключа банка minKeyLenHan=2/minKeyLenPhonetic=3 (memory.go:72).

C-7. Терминолог. terminology.go:248 — подстрочный матч без границы слова (5 потребителей); :815 sharedRunes — «общая руна = общая морфема» (для en почти всегда истинно); :337 window режет KWIC по ±N РУН без учёта границ слова (найдено верификатором) — данными не чинится ни при каком значении ширины; terminologist.go:59,61 — размеры батча/KWIC в рунах источника.

Полный список якорей ведра C:

configs/pairs/ja-ru.yaml:1 · configs/pipeline-c1.yaml:17,26,85,86
checks/cheapgates.go:146,313,435,594,603 · checks/checkers.go:224,304,393,433,435,470,501
checks/repair.go:124,131,165,197 · checks/sanitizer.go:21,133,350,433,457,706,795
chunk/chunker.go:192,217,421,439,605,629 · chunk/ingest.go:140,150,266,319,391,652
config/book.go:195 · config/pipeline.go:136,312,792,843 · lang/data/sentence-abbrev.txt:4
lang/embedded.go:25,180 · lang/langpack.go:178,486,664,870,897
membank/memory.go:72,494,505,1082 · membank/memseed.go:331
miner/miner.go:47 · miner/miner_emit.go:27,202,306 · miner/miner_palladius.go:39,128
miner/miner_patterns.go:25,188 · miner/miner_substrate.go:44
pipeline/banknote.go:33,44,175,292 · pipeline/bookrun.go:112 · pipeline/chunkrun.go:183
pipeline/disposition.go:258,259,269,271,299,310,324 · pipeline/quality.go:44,156,393
pipeline/render.go:361 · pipeline/repair.go:86,208,435,489 · pipeline/runner.go:349
pipeline/snapshot.go:431 · pipeline/stagerun.go:116 · pipeline/terminologist.go:59,61,169
pipeline/waverun.go:541 · terminology/terminology.go:248,337,386,567,815
text/norm.go:137,150,153,198 · text/runes.go:32

3.4 Ведро D — удалить (12 сайтов)

Сайт Что
cmd/tmctl/render.go:89,485 ⟨проверить⟩ продублирован глифами в CLI-строках; канон — lang/data/injection.txt:9, и он несёт два значащих ведущих пробела, которых копия не имеет ⇒ уже разошлись
configs/langpacks/zh-ru/dc-checkers.txt:7-17 11 строк cjk_numeral — третья копия универсальной константы (после lang/data/cjk-section.txt:6-19 и configs/langpacks/zh/numeral.txt); parseDCCheckers (langpack.go:897) требует её непустой от ЛЮБОЙ пары
configs/pipeline-c2.yaml:36 prompt_override на zh-ru путь в пар-генерическом конфиге; причина чисто косметическая (имя файла ≠ имя роли). Переименовать judge-selector.mdjudge.md — и строка исчезает (забукано 42)
internal/miner/miner.go:52 + miner_patterns.go:19 мёртвый вес palladius в таблице (канал не эмитится) и PackVersion = "zh-universal-v1" — поле write-only, ни одного чтения по всему репо, включая тесты
internal/miner/miner_substrate.go:123 мёртвая freq_stratum с zh-калибровкой — ни одного вызова (найдено верификатором)
internal/chunk/chunker.go:222 точный логический дубль ingest.isHeaderSeparator, синхронизируемый только комментарием
internal/checks/cheapgates.go:56,125,130 + lang/langpack.go:73,135,822 6 живых указателей на checkers_zh_ru.go — файла нет с 952469b (24.07, 7 дней назад, а не «год», как заявил агент). Вредны именно для этой инвентаризации: рекламируют пар-модуль там, где код уже generic
internal/checks/checkers.go:24 шапка утверждает, что регистр-чекер читает цель-данные; фактически registerNeg приходит из ПАР-пака (:106). Ложная документация, приводящая к двойной букировке
prompts/zh-ru/{translator-banknote,terminologist,repair/*}.md проводной формат каналов (⟦TM-BANK-v1⟧, набор типов, ⟦TM-NO-DST⟧, ⟦TM-NOCHANGE⟧) пересказан прозой в каждом пар-промпте (забукано 36г). Верификаторы переклассифицировали C→D: это не пара-зависимая величина, а инвариант движка, продублированный в пар-файл

Существенное смягчение по 36г от верификатора: заявления «мис-типизация ломает канал молча» преувеличены трижды — неизвестный тип типа падает в безопасный "term" (banknote.go:73,241), битый сепаратор ловится malformedSeparatorAt и даёт громкий parse_fail, а config/prompt_lint_test.go:22-89 УЖЕ линтует все .md под prompts/ по всем парам, включая будущий ja-ru пак, и требует настоящий таб в банкнота-промпте. Не пинятся: сам сепаратор, словарь типов, лимит строк, layout терминолога.

3.5 Ведро E — не долг (106 сайтов)

Сюда ушли: пар-строки в *_test.go и golden (гардрейл CLAUDE.md), generic-Unicode-разметка (unicode.IsLetter, IsDigit), поля book.yaml как данные (yo_policy, gender, decl), ja-ориентированные колонки схемы БД (руби), Р2/Р6-метки решений кириллицей в комментариях, configs/pairs/*.yaml как правильный адрес пар-данных, и — отдельно — internal/lang как эталон уже исполненной изоляции.

Две записи ведра E, которые стоит прочитать как предупреждения, а не как «чисто»:

  • lang/data/refusal.txt — плоский четырёхъязычный блэклист, читаемый целиком для любой книги: паттерн 抱歉[,] (частая китайская фраза) на эхнутом китайском срабатывает как ОТКАЗ, маскируя эхо; пятый язык не покрыт вовсе.
  • configs/pipeline-*.yamlни один шиппинг-конфиг не включает gates.terminology, gates.banknote, gates.repair, gates.voice, mining (grep -n 'terminology\|banknote\|repair:\|voice\|contrast_path' backend/configs/*.yaml → пусто). Это меняет тяжесть половины сайтов банкноты/терминолога: сегодня они не исполняются. Санитайзер, наоборот, включён везде (enabled: true в c1 и трёх арм-ямлах).

4. Счёт масштаба

Метрика Число
Прод-код бэкенда 83 файла / 27 102 строки (весь .go: 177 / 50 456)
Уникальных сайтов инвентаря 255 (226 от сканеров 14 дублей + 43 добора верификаторов)
ДОЛГ (A+B+C+D) 149 сайтов в 45 файлах — 133 в .go (35 файлов), 16 в данных/конфигах/промптах (10 файлов)
Из них НЕ забукано 111 из 149 (74%)
A / B / C / D / E 27 / 6 / 104 / 12 / 106
Ось долга источник+пара 63% · цель (ru) 30% · книга 3% · нет 4%
Книго-привязка поимённо 4 сайта: ingest.go:140 (60 рун по заголовкам Мастера Гу) · miner_emit.go:27 (пороги с эталонного прогона 蛊真人) · memseed.go:510 (ja-фуригана без гейта) · memseed.go:331 (нота «Polivanov» на любой паре)
Долг по пакетам checks 34 · pipeline 28 · miner 21 · chunk 17 · lang 10 · configs 9 · text 8 · membank 7 · terminology 6 · config 5 · prompts 3 · cmd 1
Живых CJK-литералов в исполняемом Go 27 строк / 12 файлов
Живых кириллических литералов в исполняемом Go 66 строк / 16 файлов (из них ~30 — русские комментарии в SQL-DDL store/migrate.go, нарушение нормы «язык кода английский», не языковая привязка)

Что эти числа говорят, а что нет. 149 сайтов — не 149 задач: класс EstimateTokens — это 11 сайтов и одна правка; цель-шов — 18 сайтов и один пакет. Честная оценка объёма работы — шесть пакетов (§9), а не полторы сотни точечных фиксов.


5. Сухой прогон второй пары: трасса отказов

Стартовые факты: configs/pairs/ = zh-ru, ja-ru, en-ru (все три есть); configs/langpacks/ = только zh/ и zh-ru/; prompts/ = только zh-ru/.

ja→ru, в порядке исполнения

  1. ГРОМКО, до БД и до провода: config/pipeline.go:899-903 — нет prompts/ja-ru/translator.md; ошибка называет ожидаемый путь; падают обе стадии одним списком. Образцовый отказ. Лечится двумя файлами.
  2. ТИХО — главный риск: runner.go:281-284 — нет каталога configs/langpacks/ja-rupack = nil, ни строки лога. Гасит разом: майнер, DC-чекеры, политику заголовков, speech-cue, sizing терминолога и LangpackVersion в снапшоте. Громко становится только под --verify-bank (mining.go:44-48).
  3. ТИХО: нет блока segmentation в pairs/ja-ru.yaml ⇒ zh-ru литералы 1797/3200/1.1978/0.3852; кана считается по фертильности кандзи ⇒ est_out завышен, чанки мельче. Направление безопасное (не обрезание), платим числом вызовов и когезией edit-юнита. Величина завышения (~1.51.7×) — гипотеза, в репо не мерена.
  4. ГРОМКО и правильно: Shift-JIS/EUC-JP отбивается ingest.go:325-329 с точным текстом. UTF-8 проходит.
  5. ТИХО, зависит от книги: 第N話 не в chapter_unit ⇒ .txt-книга станет одной главой. EPUB не затронут.
  6. ТИХО: срез банкноты безусловен (banknote.go:323), а предупреждение об отсутствующем target_script печатается только при включённом gates.banknote — которого нет ни в одном конфиге.

Работает для ja «из коробки»: эхо-гейт, CJK-leak санитайзера, гейт магнитуд 万/億, нарезка предложений с 「」, коридор coverage из пар-файла, инъекция банка через цель-данные, все isRuTarget-гейты. Снято кросс-чеком: «руби→глоссарий работает из коробки» — НЕВЕРНО, rubyToCandidates не имеет ни одного прод-вызова (проверено мной лично); боевой сид зовёт только AttachRubyAliasesToManual, которая навешивает чтение на УЖЕ вручную засиженный терм.

en→ru — другие классы

  • E-1 тот же громкий отказ по промптам.
  • E-2 сегментация глав: для .txt — вся книга одна глава (ломает окна since_ch/until_ch в memory.go:625-633 и сброс sticky в wave.go:83-87); для EPUB работает штатно. «Chapter 12» невыразимо данными: matchHeaderLine (chunker.go:186-197) не терпит пробела между маркером и цифрой и требует рун-юнит после цифр, а парсер маркера срезает хвостовой пробел (langpack.go:594). Go.
  • E-3 эхо-детектор недостижим: cjkShare = 0 на английском эхе, FlagCJKArtifact невозможен, эскалация по эху не сработает никогда. Единственный бэкстоп — detectLatinInsertion, но он SUBSTANTIVE (чанк дропается в плейсхолдер, а не чинится) и не входит в escalatable(). Опора: D19.1 (05-decisions-log.md:274) мерил эхо и на en (0/6) — то есть класс в проекте измерялся, а детектора для него физически нет. Ссылку на D19.2 из первичного разбора я снял: D19.2 явно ограничена плотным CJK-входом.
  • E-4 майнер = ноль (hanRuns пуст). Само-маскируется: при pack == nil он и так не запускается.
  • E-5 аттестация банкноты подстрокой без границы слова («art» в «start») плюс регистрозависимость (нормализация ключа регистр складывает, а сравнение по книге — нет).

Числа

Данными лечится 11 позиций (промпты пары ×2 · segmentation ·len_ratio уже есть · encoding · target_script · heading.txt · speech-cue.txt · terminology.txt · dc-checkers.txt · промпты терминолога/ремонта · частотный артефакт). Требует Go — 5 позиций: G1 монолитный манифест пака (langpack.go:178-185 + 486-492) · G2 алфавит кандидатов майнера (miner_substrate.go:44,48) · G3 детектор эха по письменности (disposition.go:271-286) · G4 детект глав только по CJK-грамматике (txt-путь) · G5 эмбед-данные вне всех хешей. Плюс 2 латентных: sourceAbbrevs("en"), ruSanitizer — на этих двух парах не стреляют. Ни одна из пяти Go-позиций не блокирует запуск — все либо усыпляют детектор, либо обесценивают механизм.

Приёмочный критерий сегодня: ja→ru проходит безопасно без единой правки Go (деградируют майнер и калибровка); en→ru проходит небезопасно — две молча спящие дыры (эхо и, на txt, главы). Устойчиво разделяет пары ровно это: G2 и G3.


6. Цены: что перекупается

Механика в трёх фактах (перепроверена построчно кросс-чеком).

  1. Снапшотов ДВА — волновые (snapshotIDForWave, snapshot.go:222), различаются подмножеством стадий и версией банка.
  2. snapshotID входит в КАЖДЫЙ RequestHash (render.go:305-308) ⇒ сдвиг = промах всех чекпойнтов волны ⇒ громкий стоп и требование --resnapshot. Собственный комментарий кода (snapshot.go:51-57): «the whole book is RE-BILLED, even for byte-identical wire requests whose verdict merely needs re-classifying».
  3. Единственный механизм не платить при сдвинутом снапшоте — точечный ре-пин, и он срабатывает только когда разошлось РОВНО memory_version (repin.go:54,61-91). Из 20 осей ре-пинуема одна.

Что это значит для вёдер:

Адресат выноса Цена Улика
configs/langpacks/<src|pair>/ перепокупка обеих волн всех книг ЭТОЙ пары, ре-пин невозможен. Хеш берётся над СЫРЫМИ байтами до парсинга ⇒ добавление комментария стоит столько же, сколько правка правила langpack.go:196,205-207,299; асимметрия с промптами, где комментарии стрипаются ДО sha256 (render.go:127-131)
internal/lang/data/ (эмбед) $0 по снапшоту — и это не льгота, а ДЫРА (см. ниже) embedded.go:20-21, хеширования нет (проверено лично)
configs/pairs/<pair>.yaml $0, если резолвнутое значение то же (фолдятся ЗНАЧЕНИЯ, не хеш файла) snapshot.go:38-46,74-83
не-фолдящийся гейт-конфиг $0, ратифицированный прецедент config/pipeline.go:305-312 (target_script)
НОВАЯ пара (configs/langpacks/ja-ru/) $0 для всех существующих книг — хеш скоупится по паре langpack.go:210-227
packAlgoVersion или новый ОБЯЗАТЕЛЬНЫЙ файл манифеста перепокупка ВСЕХ книг ВСЕХ пар + fail-loud у каждого существующего пака langpack.go:40,178-185,200-203

Ратифицированная фигура дешёвого выноса уже существует и измерена: отгрузить МЕХАНИЗМ (ридер + дефолт) — отсутствующий опциональный файл в хеш не пишется (readOptional, langpack.go:567-576), — а ФАЙЛ авторить на ближайшем оплаченном --resnapshot. Так сегодня и живут terminology.txt и speech-cue.txt: ридеры отгружены, файлов в репо нет. Прецедент цены измерен: одна строка в dc-checkers.txt двигала a7d4be2c0465133d71e8d132, и оба «нита общности» были отложены до полного resnapshot (D39.31 п.5; первичный разбор атрибутировал это D39.53 — исправлено кросс-чеком).

$0-инструмент оценки, который стоит забрать в норму. Хеш пака воспроизводим без запуска Go:

cd backend/configs/langpacks && { printf 'langpack-v2';
for f in surnames-single.txt surnames-compound.txt title-suffix.txt ordinal-title.txt rank-word.txt \
         topo-suffix.txt grade-prefix.txt numeral.txt alias-particle.txt title-formant.txt sentence-terminator.txt; do
  printf '\x00zh/%s\x00' "$f"; cat "zh/$f"; done
for f in palladius.txt palladius-phonotactics.txt; do printf '\x00zh-ru/%s\x00' "$f"; cat "zh-ru/$f"; done
printf '\x00zh-ru/heading.txt\x00'; cat zh-ru/heading.txt
printf '\x00zh-ru/dc-checkers.txt\x00'; cat zh-ru/dc-checkers.txt; } | sha256sum | cut -c1-12
# → a28ed743c99c  (совпадает с записанным langpack-v2-a28ed743c99c)

Оговорка: стендовые книги объявляют langpack_extend, поэтому в их payload едет форма langpack-v2-x… (langpack.go:391) — рецепт даёт БАЗУ.

6.1 Находка: эмбед-плоскость вне всякой дисциплины версий

internal/lang/data/*.txt — шесть файлов, ни в одном контент-хеше (проверено лично: embeddedFS используется только для ReadFile). При этом:

  • injection.txt → заголовки инъекции глоссария и редактора → байты провода;
  • target-ru.txt → санитайзер, а санитайзер есть диспозиция и переписывает финальный текст;
  • terminator.txt, cjk-section.txt → чанкер → границы чанков → провод.

Единственная защита — ручной бамп соответствующей константы. Механизма «байты ЕСТЬ версия», выстроенного для configs/langpacks/ и для trad2simp.txt, здесь НЕТ. Живая улика: коммит 8b1d8d1 добавил +59 строк в target-ru.txt и не тронул ни CheapGateVersion, ни SanitizerVersion (в том случае категории были новыми и вердикты не двигались — но механизм этого не знает). Следствие для денег: правка байта в injection.txt без бампа RenderFormatVersion меняет провод, content_hash уезжает, а projectRebill спроектирует $0 (rebill.go:137-139 пропускает юнит с неизменным снапшотом) ⇒ тихая пере-оплата вне контура согласия Р6.

Это жёсткое предусловие ко всему плану: каждый вынос литерала в данные увеличивает объём вердикт-несущего поведения, живущего в нехешируемых байтах. Без починки — «вынесли в данные» означает «сняли drift-защиту».

6.2 Правило условного фолда (выведено из прецедентов кода)

Изменение можно честно НЕ фолдить тогда и только тогда, когда его выход (1) не входит ни в один байт оплаченного запроса, (2) не входит ни в один вход разрешения диспозиции чекпойнта или final_hash, и (3) пересчитывается каждым прогоном из уже сохранённых артефактов. Ортогональное правило присутствия: любой НОВЫЙ фолд обязан быть условным по присутствию (указатель+omitempty или {enabled:false}), чтобы отгрузка механизма стоила $0 тем, кто им не пользуется.

Прецеденты, на которых это стоит: расщепление bankSliceVersion/bankParseVersion (D39.58) · DEFER фолда pack.Version() до wiring майнера (D39.16) · условный фолд секции неподписанных строк (memory.go:428-437: «re-bills nobody who had nothing to re-render») · BudgetUSD не фолдится · few-shot фолдится только при наличии ---FEWSHOT---.

Нарушители правила присутствия сегодня — две оси: style_check_version и render_format_version фолдятся БЕЗУСЛОВНО, из-за чего правка одного стилевого правила = перепокупка всех книг всех пар. Проект при этом для VoiceCheckVersion мотивировал отказ от фолда буквально этим аргументом (config/pipeline.go:266-267).


7. Синк с документацией: что заморожено ратификацией (п.7 задания)

Rewrite нельзя делать «починкой» — часть сайтов заморожена явными решениями. Список заморозок:

# Что Решение Статус
З1 ru-target-долг (isRuTarget-call-site, TokenizeCyrillic, множитель ×2) — «задокументированное ОГРАНИЧЕНИЕ, НЕ чинить этим паком; едет с канал-B/таргет-работой» D39.24 §15.3, цитируется в коде дословно (checks/repair.go:182-189) канал B закрыт (D39.2628), «таргет-работа» не открывалась. Диспозиция назначена — строка 32 бэклога (D39.34). Пункт ×2 уже погашен данными (dc_ratio dc1_unit_in_hours) ⇒ долг меньше формулировки
З2 x/text/language.Tag ОТВЕРГНУТ для гейт-предикатов: CLDR likely-subtags документированно «subject to change», 6/18 расхождений, а isRuTarget гейтит санитайзер → экспортный FinalText ⇒ нестабильность попадала бы в снапшот-ВЕРДИКТЫ D39.16 див.1 (05-decisions-log.md:599) исполнено буквально: grep -rn 'x/text/language' --include='*.go' backend/0. Причина в силе. Но появилась митигация того же класса (norm.go:69 xTextVersion + пин-тест) — если пакет захочет типизированный тег, обязан предъявить фолд версии и идти амендментом
З3 Пар-кеинг DC-чекеров — DEFER до ja→ru-реплики D39.16 див.2 в силе
З4 Фолд pack.Version() отложен был до wiring майнера — исполнен D39.16 див.3 закрыт
З5 «KEEP glosses» — CJK-глосса (羅漢拳) в русском выводе НЕ вырезается подпись владельца 16.07 (D39.2/D39.4), 6 сайтов в sanitizer.go менять границы глоссы = менять подписанное поведение
З6 Отказ от обобщения «X-X»-редупликации в правило — отвергнут как FP-опасный D20.4, cheapgates.go:361-366 прямая ловушка для «очевидного» хода «заменим список правилом»
З7 Жанровый словарь как класс ОТМЕНЁН — пустой слот в Load() есть ЗАПИСАННОЕ РЕШЕНИЕ D39.47, langpack.go:290-294 не переоткрывать под видом «данных пары»
З8 Петля ремонта построена и НЕ включается D39.38 влияет на цену пака-21
З9 Не-CJK майнер — назначен отдельный пак с прототипом mine_nonhan.py и требованиями G1G10 строка 35 бэклога (D39.37/43/50) не бесхозная заморозка, а назначенный пак

Расхождения док↔код («сломанный телефон»), найденные попутно: шапка sanitizer.go:17-20 утверждает гейт, которого у ExportNormalize нет (§3.3) · шапка checkers.go:24 утверждает цель-данные там, где приходят пар-данные · комментарий pipeline.go:832-841 утверждает, что шиппинг-конфиги ставят блок segmentation — ни один не ставит · комментарий chunkrun.go:186 «nil-inert for a no-pack book» ложен: указатель Checkers никогда не nil при ru-цели · 6 указателей на удалённый checkers_zh_ru.go.

Раскладка на 7 слоёв целевой архитектуры (09-target-architecture.md): слой 1 — сегментация/фертильность/заголовки; слой 2 — сам сеам (эталон); слой 4 — пороги банка и майнер; слой 5 — translitInterjections как единственный Go-литерал среди данными-выраженных чекеров; слой 6 — ExportNormalize без гейта; слой 7 — строка «target-aware гейты — ОТКРЫТО» держится в шапке-таблице с 26.07, то есть целевая архитектура пакет цель-шва уже ЖДЁТ.


8. Пересборка входа пака-21 «чекеры»

Нулевая находка: все якоря D39.39 устарели. Пак-19 (8b1d8d1) сдвинул checkers.go, mempostcheck.go, waverun.go. Кто возьмёт пак-21 по строкам из D-лога — попадёт мимо. Актуальные якоря (перепроверены обоими проходами):

# Дефект Актуальный якорь Корзина
1 супрессор K5c checks/checkers.go:409-412 (III)→(I) после ответа на Р2
2 атрибуция K4b checks/checkers.go:522-538 (I)
3 chevronSpeechShape checks/cheapgates.go:250-275 (II)
4 inner_marker на тире checks/cheapgates.go:209-212 (I) (новый детектор, не фикс)
5 предохранитель K5a checks/cheapgates.go:515-526 (II)
6 заглавные checks/checkers.go:446-447 (II)
7 гомоглифы / порог 3 checks/checkers.go:433,453 (II), кроме гомоглифа
8 в cheng_re configs/langpacks/zh-ru/dc-checkers.txt:65 (I), но дорогой
9 множество принятых форм membank/mempostcheck.go:144-154 (II)
10 однознаковые/подстрочные ключи membank/mempostcheck.go:80-112 + memory.go:1062-1075 (II)
11 U+0301 text/runes.go:42 + mempostcheck.go:169 (I)
12 черновик вне охвата pipeline/waverun.go:501 (III) — не дефект, забукано 46

Корзины: (I) общий класс, чинится generic-алгоритмом + данными — переживает генерализацию; (II) привязка в Go, фикс = перепроект слоя; (III) дефект постановки, а не кода.

Главный вывод пересборки — ось не та. Среди 12 дефектов собственно zh-привязок три (#5 парсер магнитуд, #8 данные пака, #10 сегментация исходника). Остальное — привязка к ЦЕЛИ (ru) и к типографике. То есть пак-21 в нынешней постановке чинит ось, ортогональную названной директивой. Это не отменяет пак — но меняет его порядок относительно пакета цель-шва.

Структурная находка сверх задания (подтверждена независимо): дефект #9 после пака-20 УХУДШИЛСЯ. miner.Term (miner_emit.go:43-55) поля Decl не имеет, attachConsolidatedDst (terminologist.go:425-437) ставит только Dst — по всему репо нет ни одного пути записи склонённых форм на майненой стороне. Значит 149 авто-строк холодного банка (D39.58) приходят с НУЛЁМ склонённых форм, и пост-чек их не примет. Это смыкается с аркой автономности банка D39.59(2) и должно решаться там, а не в паке-21.

Ещё одна проверяемая находка: на ja→ru книге БЕЗ пака lintNumberMagnitude активен с китайскими идиомными допущениями. Механика закрывается статически: runner.go:270-277 ставит CompileCheckersFor в defer, срабатывающий на всех выходах loadLangPack включая ранние return nil; magnitudeStem приходит из цель-данных ВНЕ блока if dc != nil (checkers.go:93); гейт cheapgates.go:422 проходит; cjkMagnitudeOrders читает вшитый lang.DefaultCJKSection(). Шапка cheapgates.go:57-58 «They fire 0 on a non-zh source» к нему не относится.

Р1Р4 после генерализации

Переживает? Диспозиция
Р1 гипербола 千万 расщепляется на три ДАННЫЕ (какие строки идиоматичны — в пар-пак рядом с qianwan_veto_word) — снять с владельца · ПОЛИТИКА — слить с Р2 · КНИЖНЫЙ остаток (довод построен на том, что «тот же автор в главе 2 пишет 数十万», а межглавного числового согласования в движке нет как механизма) — снять как вопрос про конкретную книгу
Р2 что есть дефект в 成 переживает целиком, и должен быть РАСШИРЕН языково-слепой контракт детектора и самый дорогой из четырёх (precision 1.000 против 0.167). Переформулировать: «детекторы единиц/шкал судят ЗНАЧЕНИЕ рендера или его ФОРМУ?» — один ответ на семейство (DC1, магнитуды, любой пересчёт), а не на иероглиф
Р3 сокращённая форма терма ФАКТИЧЕСКИ ЗАКРЫТ D39.44 Q4 уже постановил: «сокращённая форма = ПОЛЕ существующей записи, пост-чек обязан её принимать». Остаток — работа (строка 27), и это ТОТ ЖЕ шов, что #9. Рекомендация: снять с владельца, слить с #9
Р4 неразрешимая речь/мысль переживает как НОРМА, а не как вопрос про класс «у детектора бывает потолок recall by construction, он пишется в контракт правила». Правило уже де-факто действует в коде. Задать один раз, цель-слепо, и перестать переспрашивать поклассово

Итог: на подпись владельца остаются ДВА вопроса вместо четырёхР2 (расширенный до семейства единиц) и Р4 (в форме нормы).

Цена пака-21 — с исправлением

CheapGateVersion фолдится в снапшот БЕЗУСЛОВНО (snapshot.go:435) ⇒ любой из 12 фиксов сегодня = перепокупка каждой стендовой книги. Напрашивающийся «нулевой шаг» (сделать фолд условным по образцу VoiceCheckVersion) сам платный: repin.go:82-86 — «a component that was folded and no longer is — not a bank move» ⇒ moveOther. Фактическая цена, впрочем, копеечная (стендовые книги = мини-прогоны по $0.130.23).

Две поправки кросс-чека к первичной оценке, которые меняют порядок работ:

  • #9/#10 ДЕШЕВЛЕ, чем казалось: matchVersion фолдится внутрь memory_version (memory.go:383, проверено мной), а это единственная ре-пинуемая ось ⇒ стоят только реально затронутых юнитов.
  • #8 ДОРОЖЕ всех: правка dc-checkers.txt двигает LangpackVersion — отдельное поле снапшота ⇒ moveOther ⇒ перепокупка обеих волн всех zh-книг. Ратифицированный прецедент ровно об этом: D39.56 отказался шипать speech-cue.txt по той же причине.

Дешёвых пунктов, не трогающих пар-шов: #11 (U+0301) · #2 (сузить атрибуцию до спана) · #4 (inner_marker симметрично вето) · гомоглиф отдельным детектором смешанного письма · строгая форма OffLanguage (строка 28) · строка 26 в данные. Оговорка по строке 28, которую нельзя терять: последний живой замер (D39.58) утечку алфавита не воспроизвёл (0/150) и вскрыл ДРУГОЙ механизм — 甲等 → класс Цзя, целиком кириллица, строгой формой OffLanguage не ловится (это строка 36а, непроверенное поле type). Подавать строку 28 как «самый дешёвый выигрыш» нельзя.


9. План переписывания пакетами

Несущее ценовое решение, от которого зависит весь порядок: любой сдвиг снапшота = перепокупка книги, а ре-пинуема одна ось из двадцати. Значит правильная фигура — не «дешёвые пакеты вперёд», а один общий --resnapshot, в который собраны ВСЕ снапшот-двигающие правки. И делать это надо СЕЙЧАС: оплаченный корпус — два мини-прогона масштаба $0.130.23; после МАСШТАБА (7,78 млн симв.) тот же дифф будет стоить порядки.

Приёмочный критерий каждого пакета — один: «пара, которой нет в репо (ja→ru / en→ru), проходит этот слой без правки Go».

П0 — «Байты есть версия» (предусловие, обязателен первым)

Что: lang.EmbeddedVersion() = sha256 по всей эмбед-FS в фиксированном порядке, свёрнутый в снапшот рядом с LangpackVersion. ~15 строк. Зачем: без него каждый следующий пакет увеличивает объём вердикт-несущего поведения в нехешируемых байтах (§6.1). Заодно закрывает канал тихой пере-оплаты вне Р6. Цена: новая ось фолда ⇒ один resnapshot. Приёмка: правка байта в data/target-ru.txt даёт громкий resnapshot, а не тихую смену вердиктов.

П1 — Цель-шов (слой 7; целевая архитектура его уже ждёт)

Что: isRuTarget (6 call-site) → инертность-по-данным вместо Go-предиката · TokenizeCyrillic → токенизатор с алфавитом цели из данных (закрывает и #11, и самозадокументированный долг checks/repair.go:182-189) · реестр целей //go:embed data + резолв по имени вместо мапы из двух Go-строк · ruSanitizer → поле Checkers · гейт на ExportNormalize · ё→е фолд из NormalizeTargetForm в цель-данные. Цена: гейт ExportNormalize$0 (по собственной шапке не вход снапшота/вердикта). Реестр целей — $0 (поведение на ru не меняется). Токенизатор и ё-фолд двигают CheapGateVersion/memoryNormAlgoVersion ⇒ в общий resnapshot. Ратификация: ОБЯЗАТЕЛЬНА — амендмент З1 (D39.24 §15.3 «НЕ чинить»); формально проще амендировать строку 32 бэклога (D39.34). З2 соблюдать: строковые предикаты, никакого language.Tag. Приёмка: книга с target_lang: en проходит все гейты слоя 7 инертно по отсутствию данных, а не по Go-ветке; golden байт-идентичен.

П2 — Скрипт-шов источника (закрывает живой баг)

Что: cjkShare → доля рун ИСХОДНОГО письма в выходе (письмо объявляется, не нюхается) · консолидация ≥11 выражений «класс CJK» в ≥4 редакциях, с явным решением по Hangul · isCJKLang → атрибут данных. Цена: classifierVersion + SanitizerVersion ⇒ resnapshot. Не косметика: схлопывание меняет вердикт в repair.go:435 и sanitizer.go:490. Приёмка: en→ru книга, чей черновик вернул исходник дословно, даёт FlagCJKArtifact-класс и эскалируется; ko-источник перестаёт быть слепым пятном.

П3 — Нарезка и оценка (слой 1, самый wire-тяжёлый)

Что: таксономия классов EstimateTokens из данных (не два поля в Go-типе) · фолбэк segmentation = громкая ошибка вместо zh-литералов (правило D39.59 «терпимость движка к своим данным = воркэраунд») · детект глав generic (маркер-строка + цифры + опциональный юнит, с пробелом), закрывает «Chapter 12» · аллоулист кодировок из данных · sourceAbbrevs по языку книги. Цена: chunkerVersion + estimatorVersion + segmentation ⇒ resnapshot; сдвигает границы чанков ⇒ реальный провод. Самый дорогой пакет — тем важнее, чтобы он ехал в том же resnapshot, что П0П2. Приёмка: en-txt с «Chapter 12» нарезается по главам; ja-книга получает свои коэффициенты или падает громко.

П4 — Пак как механизм (слой 2)

Что: манифест по КАНАЛАМ вместо плоских 11 обязательных файлов (manifest.txt в каталоге источника; validate() требует файлы только объявленных каналов; майнер получает nil-канал и молчит) · табличный список опциональных файлов вместо четырёх копий блока read→hash→parse→assign (нетто 35 строк) · явное решение, что новые файлы не оверлеятся книгой. Цена: бамп packAlgoVersion = перепокупка всех книг всех пар ⇒ обязан ехать в общем resnapshot и только в нём. Приёмка: каталог configs/langpacks/en/ с одним объявленным каналом грузится; отсутствие каналов майнера = тишина с WARN, а не отказ и не ложь.

П5 — Майнер как семейный модуль (слой 4) — ГЕЙТ на реальную вторую пару

Что: алфавит кандидатов и длины из данных · сырой (не нормализованный) источник для ja, чтобы катакана дожила до детектора · второй ДЕТЕКТОР для пробельных языков (строка 35, прототип mine_nonhan.py, требования G1G10). Почему последним: это единственный пакет, чью правильность нельзя доказать на текущей паре. Пока нет ja/en-книги на стенде, любые решения тут — гадание. Приёмка: ja-книга даёт непустой банк с катаканными именами; парити zh EXACT сохранён.

П6 — Расщепление цены (НЕ рекомендация, а вопрос владельцу — §12)

Сводная цена и порядок.

Пакет Двигает снапшот Может ехать отдельно
П0 да (новая ось) нет — идёт первым в общем resnapshot
П1 частично ($0-части: ExportNormalize-гейт, реестр целей) $0-части — да
П2 да нет
П3 да (самый тяжёлый) нет
П4 да (все пары) нет
П5 да да — гейтится второй парой

Рекомендуемый порядок: $0-части П1 → один общий resnapshot (П0+остаток П1+П2+П3+П4) → П5 по появлении второй книги. Приборы доказательства — существующие, новых не нужно: golden байт-в-байт (internal/pipeline/golden_test.go + маскированный структурный дифф Ш-1, golden_test.go:330-336,406-425построен, вопреки записи в D-логе) · майнер-парити (TM_MINER_PARITY=1; оговорка: n=13618 и три категории пинятся точно, а recall — полосой 0.9600.970 и 古月 — только как top-50, так что «recall 0.9649 EXACT» подавать нельзя) · go test -race. Дыра приборов, которую пакет обязан закрыть своим средством: golden — книга без langpack, поэтому путь с загруженным паком не пинится байт-в-байт вовсе; banknote и repair в capture тоже отсутствуют. Для ведра A golden не защита — нужен прямой замер pack.Version() рецептом из §6.


10. Алгоритмы: что строить и чего НЕ строить (п.6 задания)

Строить (кроме уже расписанного в §9):

  • Токенизация цели — шов один: алфавит слова приходит цель-данными, unicode.Cyrillic остаётся ЗНАЧЕНИЕМ по умолчанию для ru. Все 5 мест висят на *Checkers, плумбинг не нужен.
  • EstimateTokens честно для нескольких письменностей без вендорского токенизатора — коэффициент на класс письма из данных пары; калибровка офлайн по реальным токенизаторам. Оговорка кросс-чека: в репо лежат 4 настоящих tokenizer.json (glm-4.6, qwen2.5, qwen3, deepseek-v3.2), и токенизатора БОЕВОЙ модели (deepseek-v4) среди них нет — «калибровка офлайн за $0» для неё сегодня не обеспечена.
  • internal/lang как приёмник — четыре конкретные дыры: жёсткий манифест (§9 П4), копипаста опциональных блоков, per-target реестр в двух Go-строках, нехешированная эмбед-плоскость (§9 П0). Сильные стороны не трогать: две плоскости с разными жизненными циклами, Version() = контент-хеш, fail-loud на пустую таблицу, громкая ошибка на неизвестную категорию (langpack.go:796 — поведение ЛУЧШЕ, чем описал первичный разбор).

НЕ делать:

  1. Не возвращать x/text/language.Tag — ратифицированно отвергнуто (З2), причина в силе.
  2. Не тащить UAX#29-сегментатор — он явно НЕ сегментирует идеографику (на стороне источника не решает ничего), для кириллицы почти эквивалентен «максимальному рану букв», и вносит ВТОРУЮ независимую Unicode-редакцию в вердикт-путь.
  3. Не снимать unicode.Version/xTextVersion из версионных тегов «раз скрипты теперь из данных» — таблицы всё равно тулчейновые.
  4. Не пар-кеить DC-чекеры «для симметрии»З3, DEFER до ja-пака.
  5. Не определять письмо источника/цели авто-детектом по тексту — вердикт станет функцией случайного состава входа и невоспроизводим на резюме. Объявлять, не нюхать.
  6. Не трогать кана-фолд NormalizeSourceKey ради ja — он внутри NormVersion()memory_version; катаканный сигнал возвращается подачей майнеру сырого текста, а не ослаблением общего нормализатора.
  7. Не возвращать книжные/жанровые словари в пар-слойЗ7.
  8. Не дробить internal/lang на lang/src|pair|target — число файлов не критерий сплита.
  9. Не «терпеть» собственные данные — правило владельца 31.07: тихий фолбэк вместо отсутствующих пар-данных и есть воркэраунд (сегодня их 10, поимённо в §5).

Отброшено как противоречащее нормативу: LangPack-интерфейс с реестром плагинов (реализация одна; «интерфейс объявляет потребитель») · пакет internal/script/unicodeutil (запрещённые util-имена) · дженерик-обёртки над картами пака · интерфейс TermDetector с реализациями zh/ja (возвращает пару в Go, только за интерфейсом) · лемматизатор в движок ради честного sameLexeme (новая зависимость; default-B уже ратифицированно выбросил pymorphy3-каналы — D39.12, не D39.14).


11. Критик полноты

Чего этот отчёт НЕ содержит и содержать не может:

  1. Ни одного исполнения. Сессия read-only; ни go test, ни прогон, ни замер. Всё выведено из чтения тел. Первый кандидат на проверку исполнением — активность lintNumberMagnitude на ja→ru без пака (закрывается статически, но исполнением не подтверждена).
  2. Все числовые оценки поведения второй пары — гипотезы. «est_out завышен ~1.51.7×», «чанки на 40% меньше», «завышение ~30% для en» из кода не выводятся: фертильность выведена только на zh-ru (R²=0.9633). Направление («безопасное, платим числом вызовов») — из кода; величины — нет.
  3. Оценки объёма работы в строках Go («~15 / ~35 / ~40 строк») не верифицируемы и как минимум одна посчитана по половине call-site.
  4. Ведро C — 104 сайта, и оно самое субъективное. Граница «C против E» проходит по вопросу «нужна ли этой величине пар-зависимость вообще», а он местами решается суждением. Верификаторы переклассифицировали 20 сайтов из 226; вероятно, ещё 1015 спорны.
  5. eval/ не сканировался вовсе — вне зоны бэкенда, но там живут прототипы (mine_nonhan.py) и токенизаторы, влияющие на П5 и на калибровку.
  6. Ни одна цифра recall/precision D39.39 не пере-мерена — все остаются «на одной книге, одной паре, одном жанре»; генерализация этот дисклеймер усиливает, а не снимает.
  7. Размеченный набор пакета-6 (books/gu-zhenren/labels/) лежит вне git — на приёмку пака-21 я опираюсь по описанию отчёта, не по файлу.
  8. Не оценена привязка тех чекеров, которых полигон не размечал (lintYofikation, санитайзер, DC6, транслит-междометия).
  9. Не проверялось, что происходит с ПАРАЛЛЕЛЬНЫМИ парами (две книги разных пар в одной БД) — весь анализ вёлся в предположении одной пары на прогон.
  10. Слабое место метода: сканеры дважды поверили комментариям вместо кода (шапка о «shipping configs set the whole block», шапка о гейте санитайзера) и один раз не дочитали вызовы функции (руби-канал). Все три пойманы кросс-чеком — но это ровно тот класс, который проходит незамеченным, если верификатор один. Дисциплина «автор≠ревьюер» здесь окупилась трижды.

Что в отчёт НЕ вошло, хотя агенты это заявляли (опровергнуто кросс-чеком, фиксирую, чтобы не всплыло повторно): «французский пробел молча съест экспорт» (регекс в гейтованной функции и не матчит U+202F) · «руби работает из коробки» (прод-вызовов нет) · «ja/en-книга загрузится и молча поедет на zh-числах» (падает раньше, на промптах) · «нулевой шаг стоит $0» (сам платный) · «фиксы #9/#10 дорогие» (ровно наоборот) · «Ш-1 не построен» (построен, golden_test.go:406-425).


12. СТОП. Что на владельце

Вопрос 1 (главный, бьёт по ПОСЫЛКЕ задачи). Директива названа по оси ПАРЫ, и по ней результат хороший: книго-привязки 4 сайта, вторая пара уже едет в golden, ja→ru проходит без правки Go. Но измерение говорит, что больший и более замороженный кусок долга лежит на оси ЦЕЛИ (30% всего инвентаря; среди 12 дефектов пака-21 — доминирует), и именно он заморожен D39.24 «НЕ чинить». Вопрос: пакет П1 (цель-шов) входит в эту арку или ждёт отдельной санкции? Я не могу ратифицировать амендмент чужого решения.

Вопрос 2 (деньги, решает порядок). Подтверждаете ли фигуру «один общий --resnapshot сейчас» вместо россыпи дешёвых пакетов? Аргумент: цена перепокупки сегодня — центы (корпус = два мини-прогона), после МАСШТАБА — порядки; а ре-пинуема одна ось из двадцати, так что «делать по кусочку» означает платить за каждый кусочек отдельно.

Вопрос 3 (П6, где я обязан оспорить сам себя). Напрашивается расщепление хеша пака по влиянию: heading.txt двигает провод — фолдить; dc-checkers.txt только наблюдаемость — не фолдить. Прецедент формы есть (D39.58). Но посылка «наблюдаемость — не вердикт» противоречит собственной позиции проекта, записанной дважды (snapshot.go:397-402: «editing a rule shifts the recorded style-flag counts, so a bump is a loud --resnapshot»). Прежде чем это класть на подпись, надо развести «наблюдаемость, попадающая в chunk_status/retrieval_state» и «наблюдаемость, пересчитываемую из сохранённых ответов». Подавать как готовое решение отказываюсь; подаю как вопрос.

Вопрос 4 (Р1Р4). Предлагаю снять с владельца два из четырёх: Р3 закрыт D39.44 Q4 (остаток — работа, сливается с #9), Р1 расщепляется на данные + политику (в Р2) + книжный остаток (снять). Остаются Р2 в расширенной форме («детекторы единиц судят ЗНАЧЕНИЕ или ФОРМУ?») и Р4 в форме нормы. Подтверждаете?

Вопрос 5 (пак-21). По карте пак-21 распадается: ~6 дешёвых пунктов, не трогающих пар-шов; 6 дефектов уезжают в rewrite-пакеты; #12 и K4a снимаются как не-дефекты кода; #9 уезжает в арку автономности банка. Оставляем пак-21 как единицу или растворяем в П1/П2 и арке банка?

Отдельно, вне вопросов — живой баг, который стоит починить независимо от всех решений: isCJKLang числит ko, а три детектора (cjkShare, CJK-leak, гард ремонта) Hangul не содержат. Для корейского источника движок считает нарезку как CJK, печатает предупреждение об экспозиции эха и при этом эхо увидеть не может.

Фаза 2 (переписывание) НЕ начата и не начнётся без ратификации. Ни строки кода не тронуто.