textmachine/docs/archive/reports/BANK_CONSILIUM_INPUTS_2026-09-15/03-design-v1-verification.json

430 lines
No EOL
119 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

{
"L1": {
"verdicts": [
{
"id": "F1",
"claim": "norm.go:47-48 embed; :141-153 trad2simp и катакана→хирагана безусловно; :28 скоуп «zh/ja»; у NormalizeSourceKey нет параметра языка; число мест вызова; ветки по языку нет",
"verdict": "PARTIAL",
"evidence": "Факты верны, две ссылки на строки неверны. norm.go:47-48 `//go:embed data/trad2simp.txt` / `var trad2simpRaw string`. norm.go:136 `func NormalizeSourceKey(s string) string`: параметра языка нет. :147-152 `if m, ok := trad2simp[r]; ok { r = m }` и `if r >= 0x30A1 && r <= 0x30F6 { r -= 0x60 }` без условий. Строка :28 — это «It is shared substrate: the memory bank matches and post-checks with it, the miner», слов «zh/ja» в ней нет; скоуп «zh/ja» стоит в :32 и :126. Ё-фолд, процитированный как :203-205, на деле в :198-200 (в :203 `return strings.TrimSpace`). Места вызова (python-скан не-тестового Go с вырезанными комментариями): 54 вызова в 12 файлах — decisions.go 17, mining.go 13, terminologist.go 7, banknote.go 4, memseed.go 3, memory.go 2, miner_alias 2, miner_emit 2, по одному в memconsistency, miner_patterns, miner_substrate, bankdecisions. Ещё 2 передачи функцией (terminologist.go:517 в ParseReply, :697 в ParseTypes) и контракт хранимого ключа store/migrate.go:460. Ветки по языку: греп `SourceLang|source_lang|\"ja\"|\"zh\"` по internal/text, membank, miner, terminology дал 0 (контроль: просканировано 23 не-тестовых go-файла).",
"correction": "Скоуп «zh/ja» — norm.go:32 и :126. Ё→е — :198-200. Мест вызова 54 в 12 файлах плюс 2 передачи функцией. Самый крупный потребитель, дверь membank/decisions.go (17 вызовов), чистая функция без пака: mining.go:29-31 прямо говорит, что bank-apply Runner не строит."
},
{
"id": "F2",
"claim": "Нормализация «ломает японский»: 後→后, 乾→干, 髮→发; ハル=はる; японская книга идёт через тот же NormalizeSourceKey; терминолог получает нормализованный src",
"verdict": "PARTIAL",
"evidence": "Класс верен, примеры неверны. Контроль: в trad2simp.txt 508 пар. 後, 乾, 髮 в таблице ОТСУТСТВУЮТ (python-лукап: «後 -> (absent)», «乾 -> (absent)», «髮 -> (absent)»), шапка таблицы :13-14 прямо исключает «後→后 vs 后→后». Пар, где обе стороны кодируются в JIS X 0208 (shift_jis), — 68. Среди них живые японские кандзи с разным значением: 機→机 (машина/стол), 葉→叶, 準→准, 築→筑, 範→范, 僕→仆, 據→据, 莊→庄. enkan_no_hate_ja.txt (60 127 знаков после NFKC, из них 14 909 Han): переписано 1 798 Han-рун (12,1%), топ — 見→见 217, 葉→叶 118, 誰→谁 116, 機→机 15, 築 10, 準 3; ещё 2 558 знаков катаканы свёрнуты в хирагану. Пример ハル/はる: memory.go:73 `minKeyLenPhonetic = 3`, а по :78-83 ключ из 2 каны не срабатывает никогда. Ключи из 3 и более каны срабатывают, но collisionProneKey (memory.go:88-90) делает их AMBIGUOUS, KeyTrusted=false. Исполнимость пары: в configs/langpacks только ru, zh, zh-ru; в backend/prompts только zh-ru; config/pipeline.go:1038 отказывает: «no prompt for pair %q role %q ... never silently substitute another pair's conventions» (то же пишет books/gu-zhenren/door-to-file/poscontrol-paid-holes/book.yaml:34-40). Ветки по языку нет, поэтому куски любой книги проходят через memory.go:574 и mining.go:82. Вход модели: строки `ctx:` — окна по NSource (terminology.go:375-396, источник mining.go:130 `NSource: c.NSource`), всегда нормализованы. Поле `### src` (terminology.go:773) нормализовано у майненых кандидатов (terminologist.go:1052-1056: «a substring of already-normalized chunk text and therefore equals its own key») и сырое, как написал черновик, у банкнотных (banknote.go:532 `Observed{Key: key, Src: a.src}`). В листе стопа прогона B банкнотных 64 из 66 (origin=banknote 64, both 2).",
"correction": "Ложные тождества и чужое письмо в контекстах для ja реальны; нужные примеры — 機/机, 葉/叶, 築/筑; ハル/はる не срабатывает, а サクラ/さくら срабатывает как AMBIGUOUS. Пары ja в репо нет, поэтому вред перспективный: это ровно вопрос §Цели п.2, а не наблюдаемая поломка."
},
{
"id": "F3",
"claim": "pack.Version() фолдит байты пакета в снапшот (langpack.go:204); memoryNormVersion хеширует trad2simpRaw; перенос таблицы в langpack сохраняет громкий --resnapshot",
"verdict": "PARTIAL",
"evidence": "langpack.go:204 `func (p *Pack) Version() string { return p.version }`; Load фолдит каждый прочитанный файл (langpack.go:237-238); снапшот получает его в snapshot.go:409/477 `LangpackVersion: r.packVersion()`. norm.go:83 `sha256.Sum256([]byte(memoryNormAlgoVersion + \"\\x00\" + trad2simpRaw))`. НО сегодня NormVersion попадает в снапшот ТОЛЬКО внутри memory_version (membank/memory.go:417 `h.Write([]byte(text.NormVersion() + ...))`); отдельного norm-поля в снапшоте нет (поля snapshot.go: langpack_version :409, embedded_version :418, memory_version :425). repin.go classifySnapshotMove: любой другой изменившийся ключ даёт `moveOther`, а тогда «Nothing is re-pinnable». Итог: правка таблицы сегодня — перемещение только банка, и незатронутые юниты пере-пиниваются за $0 (volume.go rowsResumeFree :798-829). Через pack.Version() та же правка сдвигает langpack_version, это moveOther, и перекупается каждый сохранённый юнит. Кроме того, runner.go:450-456 возвращает `\"\"` при отсутствии пака, а config/book.go:93-98 держит `langpack_root` необязательным: zh-книга без пака осталась бы без таблицы. «Прецедент» lang-script.txt — встроенные Go-данные (lang/embedded.go:22 `//go:embed ... data/lang-script.txt`), их фолдит EmbeddedVersion, а не pack.Version(); по природе это тот же артефакт, что text/data/trad2simp.txt.",
"correction": "Для книг с паком громкость сохраняется, но класс цены хуже: полная перекупка вместо $0-репина незатронутых юнитов. Этого не будет, если хеш таблицы Identity фолдить в memory_version, а не в pack.Version(). Книгам без пака нужна дефолтная Identity или обязательный пак. lang-script.txt не прецедент выноса в langpack."
},
{
"id": "F4",
"claim": "UNIQUE по сырому src (migrate.go:203,389); матчер ключуется только по src, sense в рантайме не различается (memseed.go:203-207)",
"verdict": "CONFIRMED",
"evidence": "store/migrate.go:203 `UNIQUE (book_id, src, sense, since_ch, until_ch)` (glossary); в :389 тот же кортеж, но у voice_profiles. Алиасы: `UNIQUE (book_id, term_id, alias)`, комментарий «raw; normalized in Go for matching». memseed.go:203-207: «The deterministic matcher keys ONLY on src (it cannot disambiguate sense within a chunk), so BOTH rows fire together». memory.go:357 `b.keyOwners[nk] = append(b.keyOwners[nk], idx)`, memory.go:592 `for _, ei := range b.keyOwners[k]`; Select поле sense не читает. Оговорка: строки движка хранят в src уже нормализованный ключ (mining.go:612 `miner.Term{Src: c.Key, ...}`), сид — сырой.",
"correction": "Строка 389 — это voice_profiles, а не glossary."
},
{
"id": "F5",
"claim": "Преемник: главы ≤N не перегенерируются — строка провода не меняется ⇒ $0-репин (volume.go:798-840, repin.go:305-327)",
"verdict": "PARTIAL",
"evidence": "repin.go:305-329 repinnable → bankOnlyMove; volume.go:798-829 rowsResumeFree требует `h == cs.ContentHash` и repinnable; хеш — по пере-отрендеренным сообщениям (repin.go:131-160, `MessagesWithInjection(..., injection[st.Role])`). Проба через overlay в internal/membank (дерево не тронуто, git status 0): старая draft-строка [0,0]→[0,4], одобренный преемник [5,0], кусок «古月山寨里,方源说道:好。方源又说。», бюджеты 800 и 8. Главы 1 и 4: editorLines побайтно те же («- 方源 → «Фан Юань»\\n- 古月山寨 → «деревня Гуюэ»»), memory_version сдвинулся. Главы 5 и 8: строка сменилась на «- 方源 → «Старый Фан»», ambiguous→confirmed. Для ≤N1 меняется только id записи (memory.go:309, в id входит until_ch), а id не рендерится. Сам репин идёт через --resnapshot (volume.go:351). Изъяны: (1) off-by-one в тексте проекта: «с главы N … until=N1» и тут же «Главы ≤N не трогаются», хотя глава N меняется. (2) СИД-термин (origin given) дельтой не преемствуется. Проба `ApprovedSharedKeyCollisions`: одобренный сид [0,0] + одобренный преемник [5,0] → «firing key \"方源\" is shared by different terms … with overlapping spoiler windows»; этот гард роняет прогон (bankmaterialize.go:146-148), и та же проверка стоит в вердикте двери (decisions.go:913-914). С неподписанным преемником коллизии нет, но в главе 8 editorLines = [«- 方源 → «Фан Юань»»]: уступка по memory.go:925, преемник молча инертен. Закрыть окно сида — это правка сида, а дверь её отклоняет (decisions.go:358-363: «Editing a seed term means editing glossary_seed, which moves the BASE snapshot and re-pays the draft wave») и контракт её исключает (openapi.yaml:2370-2372). (3) Закрыть окно строки ДВИЖКА — значит писать в авто-банк, у которого единственный писатель движок (таблица 18-bank-ontology.md); на проводе это работает только через уступку неподписанной строки подписанной.",
"correction": "Главы ≤N1 не трогаются, глава N пере-редактируется. $0-репин держится для строк движка и дельты при сдвиге только банка с --resnapshot. Для сид- и given-терминов преемник требует правки сида: базовый снапшот сдвигается, и черновая волна перекупается на кусках ≥N с этим термином. Это противоречит (e) «сид не трогается никогда» и (f) «черновая волна не перекупается никогда»."
},
{
"id": "F6",
"claim": "Дверь: decisions.go:33-34 два глагола, :50-57 DecisionDepth=edit_wave, род не принимается, окно одним решением не правится (:86-92); контракт BankCorrection с закрытым словарём action; platform/internal/runs/bank.go:90 пробрасывает документ в tmctl bank-apply",
"verdict": "CONFIRMED",
"evidence": "decisions.go:33 «TWO VERBS AND NO THIRD. `approve` … `decline`»; :57 `const DecisionDepth = \"edit_wave\"`; :80-84 про род: «It is still NOT ACCEPTED … correcting one stays a seed edit»; :86-88 «a term's chapter WINDOW cannot be corrected in one decision … declining removes every window of the surface». openapi.yaml:2348-2350 `BankCorrection: type: object, additionalProperties: false`; :2418-2420 `action: type: string, enum: [approve, decline]`; :2375-2377 окно — операция в два вызова. platform/internal/runs/bank.go:88 ApplyBankCorrections, :141 `ingest.EncodeDecisions(in.BookID, in.Decisions)`, :160 `s.Bank.BankApply(ctx, s.Cfg.EngineBinary, book.Workdir, path, in.Preview)`. «Контрактный минор» для нового глагола согласуется с политикой: openapi.yaml:112 «While the version is 0.x, a MINOR bump is the lane for breaking changes».",
"correction": "Запуск verb — runs/bank.go:160 (кодирование — :141), а не :90."
},
{
"id": "F7",
"claim": "terminology.go:1115-1121 → miner_emit.go:261-269: ⟦TM-NO-DST⟧ отдаёт закон догадке черновика; «не термин» и «90» становятся передачей; Evidence майнера (terminology.go:106) детерминирован — какая доля термов прогона B его несёт",
"verdict": "CONFIRMED",
"evidence": "Проба ParseReply через overlay (internal/terminology, target unicode.Cyrillic): «方正\\tне термин\\t90» → dst=«не термин», bad=0; «方正\\t90» → dst=«90», bad=0; «方正 90» → dst=«90»; «方正\\tне является термином\\t80» → принято; «方正\\t⟦TM-NO-DST⟧\\t40» → dst=\"\". Причины: wellFormedLemma (terminology.go:643-656) пропускает всё от 2 рун без дефиса по краям и скобок; OffLanguage (script.go:31-46) возвращает false, если букв нет («90»). Проба DeltaFile (internal/miner): отклонённый термин с предложением черновика → `dst=\"Фан Чжэн\" status=\"auto\" note=\"dst PROPOSED by the translator (banknote, 1 chunk(s)) — NOT approved…\"` (miner_emit.go:261-266). Проба membank: auto-строка с dst → injected=1, editorLines «- 方源 → «Фан Юань»» (memory.go:315-327 делает строку матчибельной при непустом dst, editorLines рендерит строку любого статуса). Значит комментарий terminology.go:692-693 «A declined term emits status:auto (inert) instead of a guess» ложен. Промт сам просит этот ответ: prompts/zh-ru/terminologist.md:36-37. В оплаченных прогонах: 0 строк NoDst, 0 цифровых передач, 0 передач со словом «термин» (контроль: 129 строк ответов в B и 137 в A; авто-банк 64 в B и 67 в A, все draft) — дефект латентный. Evidence детерминирован (паттерн-канал miner.go:100-121 поверх нормализованных кусков), но в листе стопа B строка `evidence:` есть у 2 из 66 кандидатов: 方正 [surname:方], 沈翠 [surname:沈]. В авто-банке у 62 из 64 заметок тег «banknote-only candidate (the miner did not surface it)» — не паттерн, его дописывает эмиссия (mining.go:607-612).",
"correction": "Код подтверждает проект. Замечание к проекту: детерминированная улика есть у ~3% кандидатов (см. D-d3)."
},
{
"id": "F8",
"claim": "Улики терминологу — первые три вхождения по порядку (terminology.go:336-400); что нужно, чтобы брать выборку по всей книге без роста цены выше «центов»",
"verdict": "CONFIRMED",
"evidence": "terminology.go:340-352: AttachKWIC сортирует куски по (chapter, chunk_idx); kwicFor (:375-396) набирает вхождения с начала книги до maxPer. Дефолты terminologist.go:75-76: `terminologyDefaultKWICPer = 3`, `terminologyDefaultKWICWidth = 40`; ручки config/pipeline.go:345-346 и terminology.txt пака. Что нужно для выборки: (1) детерминированный стратифицированный селектор в AttachKWIC ($0); (2) позиции по вариантам — у Proposal только счётчик (terminology.go:62 `Chunks int`; banknote.go:504 `cur.Chunks++`), поэтому «вокруг расхождений» требует протащить id кусков через свёртку банкноты → Observed → Variant; (3) стоп читает только уже начерненные куски (volume.go:338-340 «consolidates over the chunks drafted SO FAR»), так что «вся книга» — это чтение не купленного исходника; (4) смена байт запроса или состава батча один раз промахивается мимо чекпойнтов на книгу (комментарий dropBankSettled, terminologist.go:1160-1168). Цена по прогону B: терминолог — 8 вызовов, prompt 27 445 ток. (кэш 5 376), completion 21 394 ток., $0.03803 по $0.44 / $0.014 / $1.32 за M (models.yaml:230); промпт-сторона $0.0097 (25%), completion $0.0282. Классификатор ($0.04531) читает те же батчи. Даже если весь некэшированный промпт считать контекстами, 4× контекстов добавит ≈$0.03 на 66 кандидатов (≈$0.00044 за кандидата) и ≈$0.18 на ~400 кандидатов (два капа по 200) за консолидацию.",
"correction": "«Центы» за консолидацию правдоподобны (оценка, не замер), но нужны протяжка позиций и одна перекупка прохода на книгу; при сегодняшней пере-консолидации на каждой покупке цена множится на число покупок."
},
{
"id": "A-a2",
"claim": "ё→е зашито на целевой стороне (norm.go:203-205) и стоит в очереди владельца как данные (D39.64 §5, ряд 14)",
"verdict": "PARTIAL",
"evidence": "Ё-фолд в norm.go:198-200 (`if r == 'ё' { r = 'е' }`); в :203 уже `return strings.TrimSpace(b.String())`. BACKLOG.md строка 28, ряд 14: «ё-фолд → target-данные (`char_fold`; … сдвиг `memory_version`)», источник D39.64 — верно.",
"correction": "norm.go:198-200."
},
{
"id": "A-a3",
"claim": "Загрузчик авто-банка сравнивает сырым кортежем (mining.go:717-733); уступка на проводе — по сырому src (memory.go:905,925)",
"verdict": "CONFIRMED",
"evidence": "mining.go:717-733 `type ukey struct { src, sense string; since, until int }` и `held[ukey{e.Src, e.Sense, e.SinceCh, e.UntilCh}]` (реджекты при этом нормализуются, :726). memory.go:905 `signedSrc[strings.TrimSpace(p.entry.src)] = true`, :925 `if p.entry.status != \"approved\" && signedSrc[src]`.",
"correction": ""
},
{
"id": "A-a4",
"claim": "«Хранимый src остаётся авторским — для показа»",
"verdict": "REFUTED",
"evidence": "Сегодня строки движка хранят НОРМАЛИЗОВАННЫЙ ключ: mining.go:612 `out = append(out, miner.Term{Src: c.Key, ...})`; terminologist.go:1052-1056 — майненый кандидат приходит с поверхностью майнера, а это подстрока нормализованного текста. Память стопа тоже нормализована: store/migrate.go:460 `surface TEXT NOT NULL, -- text.NormalizeSourceKey form`. Майнер работает только по NSource (mining.go:82), сырую поверхность майненого кандидата не сохраняет вовсе.",
"correction": "«Остаётся» верно только для сида. Чтобы строки движка хранили авторский src, нужен новый путь захвата поверхности; смена Identity перевыключает хранимое состояние (bank_stop_presented, src авто-банка), то есть это миграция данных, а не только --resnapshot."
},
{
"id": "B-b1",
"claim": "Схема уже держит (Identity, sense, окно): UNIQUE migrate.go:203; окна memory.go:681-689",
"verdict": "CONFIRMED",
"evidence": "store/migrate.go:203 `UNIQUE (book_id, src, sense, since_ch, until_ch)`; memory.go:681-689 spoilerBlocked: `if e.sinceCh > 0 && chapter < e.sinceCh` / `if e.untilCh > 0 && chapter > e.untilCh`. Оговорка: UNIQUE стоит на сыром src, а не на Identity (см. F4).",
"correction": ""
},
{
"id": "B-b3",
"claim": "Оговорку области употребления обещал D39.104 п.1; в editor.md её нет — только :5 про идиомы и :7 «СТРОГО»",
"verdict": "CONFIRMED",
"evidence": "05-decisions-log.md:359 (D39.104 п.1): «получают узкую оговорку ОБЛАСТИ употребления, действующую на все строки одинаково». Греп «област» в backend/prompts/zh-ru/editor.md — 0 (контроль: 41 строка), по всем 7 файлам prompts/zh-ru/*.md — 0. editor.md:5 — кальки жестов и идиом; :7 «СТРОГО по приведённому глоссарию». Нюанс: D39.104 п.2 откладывает внесение в движок до строки бэклога 134 «ПОСЛЕ отчёта пробы»; в живом BACKLOG.md строки 134 нет (0 из 314 нумерованных), её судьба не прослежена.",
"correction": ""
},
{
"id": "D-d1",
"claim": "Сегодня 家族/族 режет срез top-200 ДО типа (miner_emit.go:126-129)",
"verdict": "REFUTED",
"evidence": "Кап действительно стоит до фильтра приемлемости (miner_emit.go:126-129 `if len(ranked) > emitRankCap { ranked = ranked[:emitRankCap] }`), но названные термины режутся не им. emissionEligible, miner_emit.go:346 `if !hasAnyType(c.Types, \"name\", \"place\", \"title\") { return false }` и :349 `runeLen(c.Src) < 2`. BACKLOG ряд 440, опровержение 12.09: «`族` отсечён БЕЗУСЛОВНО как ОДНОРУННЫЙ … `家族` отсечён ТИПОМ — у него 2 руны и freq 9 при emitMinFreq 5».",
"correction": "Обычные существительные сегодня закрывает тип-фильтр name|place|title и запрет однорунных, а не срез top-200; подмена механизма уже опровергнута в ряду 440."
},
{
"id": "D-d3",
"claim": "Предохранитель «имя выкинуть нельзя» — детерминированная улика майнера (Evidence, terminology.go:106): при ней not-a-term запрещён",
"verdict": "REFUTED",
"evidence": "Детерминизм подтверждён (F7), достаточность — нет. Лист стопа прогона B: `evidence:` у 2 из 66 кандидатов (方正, 沈翠 — surname). Авто-банк B: 16 строк name/place, из них 14 без паттерн-улики: 凤雏, 华夏, 卧龙, 古月一族, 古月方源, 宗族祠堂, 宗祖祠堂, 方家, 方老魔, 沈嬷嬷, 熊家寨, 白凝冰(→Бай Нинбин), 白家寨, 青茅山. Поле Evidence двусмысленно: при эмиссии 62 из 64 строк получают в него не-паттерн «banknote-only candidate (the miner did not surface it)» (mining.go:607-612). Гард по непустому Evidence на эмиссии запретил бы not-a-term на всём, гард по входу терминолога защитил бы 2 из 66.",
"correction": "Детерминированная улика имени покрывает ~3% кандидатов; принцип «не обеднять язык без человека» на ней не держится. Нужен другой источник улики (паттерны по банкнотным кандидатам, тип после классификатора) или человек."
},
{
"id": "E-e3",
"claim": "Дверь пишет только дельту (Source:mined), до черновиков не доходит (decisions.go:50-57, bankmaterialize.go:318-333); сид не трогается никогда ⇒ «пол книги перегенерировать» невозможно",
"verdict": "PARTIAL",
"evidence": "Путь дельты подтверждён: decisions.go:57 `DecisionDepth = \"edit_wave\"`; bankmaterialize.go:318-333 — базовый банк исключает `row.Source == \"mined\"`. Но преемник для сид-термина дельтой невозможен (коллизия или молчаливая инертность, пробы в F5), а правку сида дверь отклоняет (decisions.go:358-363).",
"correction": "Для сид- и given-терминов пункт (e) недостижим без правки сида, а она сдвигает базовый снапшот и перекупает черновую волну на затронутых кусках."
},
{
"id": "F-f1",
"claim": "Платформа читает только terms (platform/internal/ingest/bank.go:57-70)",
"verdict": "CONFIRMED",
"evidence": "В ingest/bank.go:57-70 `wireBank` содержит только Version, BookID и Terms. Греп `json:\"proposed` по platform — 0 (контроль: 209 go-файлов). Движок секцию публикует: backend/internal/pipeline/bankexport.go:64 `Proposed []BankExportProposal json:\"proposed,omitempty\"`; ряд 224 фиксирует то же.",
"correction": ""
},
{
"id": "F-f2",
"claim": "Черновик по-прежнему без движковых строк",
"verdict": "CONFIRMED",
"evidence": "bankmaterialize.go:318-333: `if row.Source == \"mined\" { hasMined = true; continue }` → `r.baseMemory = MaterializeBank(baseIn, ...)`; черновая волна выбирает по базовому банку.",
"correction": ""
}
],
"extra_findings": [
{
"title": "Механизм преемника не применим к подписанным сид- и given-терминам: получается либо падение прогона, либо молча инертная строка",
"evidence": "Проба через overlay (internal/membank). ApprovedSharedKeyCollisions над одобренным сидом 方源 [0,0] и одобренным преемником [5,0] → «firing key \"方源\" is shared by different terms … with overlapping spoiler windows»; bankmaterialize.go:146-148 роняет на этом прогон, вердикт двери делает ту же проверку (decisions.go:913-914). С неподписанным преемником коллизии нет, но в главе 8 editorLines = [«- 方源 → «Фан Юань»»] (уступка по memory.go:925). Закрыть окно сида дверь не даёт (decisions.go:358-363), контракт тоже (openapi.yaml:2370-2372, origin given).",
"impact": "Центральные имена книги, заданные сидом, не получают «один глагол succeed» без правки сида. Правка сдвигает базовый снапшот и перекупает черновики на кусках ≥N — прямо против (e) и (f). Пакет 1 должен либо разрешить преемнику закрывать сид-окно (новый писатель сида), либо честно исключить given-термины."
},
{
"title": "Вынос таблицы через pack.Version() меняет класс цены правки таблицы: $0-репин превращается в полную перекупку",
"evidence": "Сегодня NormVersion фолдится только в memory_version (memory.go:417); отдельного поля в снапшоте нет (snapshot.go:409 langpack_version, :425 memory_version). repin.go classifySnapshotMove: любой изменившийся ключ, кроме memory_version, даёт moveOther («Nothing is re-pinnable»). pack.Version() — это langpack_version (snapshot.go:477).",
"impact": "Любая правка таблицы свёртки для пары перекупает все юниты обеих волн у всех книг пары, а не только юниты с изменившимися байтами. В трейдоффах проекта это не названо. Лечится фолдом хеша Identity в memory_version, а не в хеш пака."
},
{
"title": "В хранилище лежат уже нормализованные ключи: смена Identity — миграция данных, а не только --resnapshot",
"evidence": "store/migrate.go:460 `bank_stop_presented.surface -- text.NormalizeSourceKey form`; mining.go:612 `miner.Term{Src: c.Key}`; terminologist.go:1052-1056 (src майненого кандидата — нормализованная подстрока).",
"impact": "Для zh-simplified вывод не изменится, но для любой пары, где Identity станет отличаться от сегодняшней свёртки, память стопа (стоп на новизне) и src авто-банка перевыключаются — будут ложные «новые» кластеры и повторные стопы. Обещание «хранимый src авторский» требует нового захвата поверхности в майнере."
},
{
"title": "«Терминолог только для новых Identity» гасит споры, которые сегодня выдаёт ответ роли, а замену проект кладёт позже",
"evidence": "terminologist.go:1127-1134, замер: «439 candidates sit on a surface the seed holds, and in 366 of them at least one draft disagreed … A one-condition filter would have bought a saving by silencing 366 disputes». CanonConflicts (terminology.go:929-932) без consolidated возвращает nil. В проекте терминолог «только по новым Identity» стоит в Пакете 1, а «детектор споров (данные пары)» — в Пакете 2.",
"impact": "Между пакетами лист подписи теряет метки противоречий с банком на ~27% кандидатов корпуса (366 из 1365). Бесплатное сравнение черновик↔закон должно войти в тот же пакет, что и фильтр."
},
{
"title": "Нить Identity через код дороже заявленного: дверь и сид-линт работают без пака, книги без пака остаются без таблицы",
"evidence": "54 вызова в 12 файлах плюс 2 передачи функцией (F1); membank/decisions.go (17 вызовов) — чистая функция, bank-apply Runner не строит (mining.go:29-31); runner.go:450-456 `packVersion … \"\" when not`; config/book.go:93-98 — langpack_root необязателен.",
"impact": "Пакету 1 нужно протащить пак (или объект Identity) в дверь, сид-линт, майнер, терминолога и материализацию, а для книг без пака определить дефолт. Иначе zh-книга без langpack_root молча теряет trad→simp после одного громкого пере-снапшота."
}
],
"obstacles": "Не проверено или слепые места: (1) Преемник проверен пробами на уровне membank (Select и editorLines, ApprovedSharedKeyCollisions), а не сквозным прогоном через renderedContentHashes и rowsResumeFree с настоящими шаблонами. То, что content_hash зависит только от отрендеренного текста, выведено из кода repin.go:131-160. (2) Отказывает ли вердикт двери на коллизии, внесённой решением (compare-by-subject), или записывает её — не прослежено; установлено только, что проверка идёт (decisions.go:913-914). (3) «Живой японский кандзи с другим значением» я определял по кодированию в JIS X 0208 (python shift_jis) и собственному знанию значений; списка Jōyō офлайн нет. (4) Цена выборки по всей книге — оценка из разбиения токенов прогона B, не замер; доля контекстов в промпте не выделена, потому что тексты запросов в БД не хранятся (checkpoints хранят только ответы). (5) Ряд 134 (носитель D39.104 п.2) в живом BACKLOG не найден (0 из 314) — в архивных слайсах не искал. (6) Формулировки «одна платная книга» в docs нет; D39.63 п.3 (01.08) говорит «боевых книг в полёте нет». Идут ли сейчас платные книги через платформу, не проверял, поэтому утверждение раздела 3 про «$0» не вердиктовано. (7) Разделы 0, 2, 3, 4 проекта проверены только там, где пересекаются с F1F8. (8) Кейсы «90» и «не термин» доказаны пробой парсера; в оплаченных прогонах A и B таких ответов 0 (контроль: 129 и 137 строк), так что частота в бою неизвестна. Пробы запускались через go test -overlay из файлов в scratchpad; дерево /home/ubuntu/tm-verify-1509 не менялось (git status --short: 0 до и после)."
},
"L2": {
"verdicts": [
{
"id": "R1",
"claim": "The auto-bank goes from 'this run's projection' to an append journal, and the bank becomes a deterministic fold of the owner's and engine's journals. The claim is that ontology 18 survives literally and only the engine source changes, becoming cumulative 'like every other source'.",
"verdict": "PARTIAL",
"evidence": "What survives is real. Each source keeps one writer (18-bank-ontology.md:13-15), the view is still rebuilt whole (:16, :20-22), and a fold of journals is still a full rebuild.\n\nThe framing is wrong. In the ontology the auto-bank is a SOURCE, not a projection: 18:15 says '`<project_db>.auto-bank.yaml` (слово ДВИЖКА, сидит при базе)', writer 'прогон'. The phrase 'проекция этого прогона' appears nowhere in docs or backend code (0 hits); the control is that the ontology names auto-bank twice, both times as a source or the loop.\n\nFive places need a note:\n(a) 18:17 says 'остановившийся прогон авто-банк не пишет', and the code agrees: mining.go:234-249 `if !stopping { writeAutoBank … seedGlossary }`. The terminologist runs before the stop decision (mining.go:165) and nothing from it is kept as a source on the stop. So the design's 'реплей-пины (стоп→подпись→резюм)' and 'повтор прогона реплеит события, а не модель' require writing events at the stop boundary. That changes the clause.\n(b) The loop, 18:24-29, is guarded by `unsignedEngineSurfaces`. Its contract (mining.go:752-758) is to keep offering the engine's own unsigned rows 'until the owner PROMOTES … or DECLINES'. The design's 'терминолог вызывается только для Identity без действующего закона в окне' turns the engine's past unsigned decisions into durable law that stops re-asking across runs. The ontology does not state that property.\n(c) Successor on an engine term: the door writes only delta and rejects (18:15; decisions.go:33 'TWO VERBS AND NO THIRD'). Closing the engine window (until=N1) inside the engine journal would need a second writer, so the fold needs a new rule, 'an owner event cuts off engine law'. Today that rule exists only on the editor wire (memory.go:926 'an unsigned rendering yields to the signed canon'). The loader only warns on approved×unsigned (seeding.go:72) and fails on approved×approved (bankmaterialize.go:146).\n(d) Format: mining.go:763 says the auto-bank uses the seed schema 'so `tmctl seed-lint` reads it, and a row can be moved into the owner's delta by copy-paste'. An event journal with outcome and provenance breaks that.\n(e) 'Накопительный, как и все прочие' does not hold. The delta is overwritten, not appended: decisions.go:35 'a decision is REPLACED by the opposite one', and mergeTerm overwrites the row.",
"correction": "Say it plainly: the ontology changes, and it needs a note. Rewrite 18:15-17 so the engine source is a durable journal, written on the stop boundary too. Rewrite the loop paragraph 18:24-29: the loop now carries unsigned engine decisions forward between runs, and `unsignedEngineSurfaces` changes meaning. Add a precedence rule to the view (owner event over engine law, including window truncation) with a pin. Record that the engine file leaves the seed schema, or keep the journal as a separate file next to a seed-compatible view."
},
{
"id": "R2",
"claim": "The closed outcome vocabulary (law / not-a-term / polysemous / undecided) fits D39.42 §C2-7 (two-mode emission), D39.104 п.1 (law whatever the status, with an area-of-use carve-out) and D39.144 (one OK for the whole bank, stop only on novelty).",
"verdict": "PARTIAL",
"evidence": "D39.42 §C2-7 (research/20-bank-mining.md:604-608) allows only 'status: auto (без dst — инертны)' or 'status: draft', 'в схеме seedTerm … БЕЗ новых полей', with Source class and evidence kept in a separate signature map. D39.42 п.1 (log:275) defines the terminologist's output as 'консолидированное dst на каждый терм'. `law` maps to draft and `undecided` to auto without dst. `not-a-term` and `polysemous` have no form in the status machine (migrate.go:192 auto|draft|approved). They either collapse into auto, indistinguishable from undecided, or need new fields, which breaks 'БЕЗ новых полей'. Either way D39.42 п.1 and §C2-7 need amending.\n\nToday's behaviour sharpens this. miner_emit.go:261-269 copies the banknote proposal into st.Dst, and status becomes draft only when a consolidated dst exists (:276-279). D39.104 makes the wire blind to status (editorLines skips only dst==\"\"). So the §C2-7 promise 'never on a raw per-chunk banknote guess' (miner_emit.go:271-275) holds on status only, and on the wire the guess rides as law. 'Не-закон' for undecided/not-a-term therefore means changing what an auto row may carry.\n\nD39.104: there is no clash on the wire. The carve-out, though, is ratified only as doctrine (log:359). Its sole design was a probe arm 'метаязыковое окно (оговорка области + leakage)' (archive/05-decisions-D39-91-105.md:119). D39.104 itself names the leftover risk 'оговорка как лазейка — под leakage-метрикой пробы'. It is not on the wire (injection.txt editor_header: 'не вводи иных вариантов') and not in editor.md (:7 'СТРОГО'). Its scope is narrow: 'игра слов · разбор знаков · буквальное не-терминное прочтение'. The design makes it 'обязательная часть архитектуры' and the mechanism for name versus ordinary word, without naming the leakage gate. (Search in 13 files: 3 hits, none dropping the clause.)\n\nD39.144 and D39.47: in section 1(f) the `proposed` list is 'новое, undecided, polysemous, предложения преемников, споры', with no `not-a-term`. So an engine decision to throw a term out never reaches what the single OK covers. That goes against D39.47, 'решение о переводе принадлежит ПОДПИСИ владельца', and ontology 18:33-36, 'какие переводы человек действительно одобрил'. Separately, D39.144 п.1 and backlog row 192 say of the post-reading edit cycle 'НЕ проектировать до полигонных итогов', yet the design puts `succeed` plus a contract minor into package 1. I found no conflict with the novelty stop (D39.158 п.1): re-asking about `undecided` goes to the terminologist, not to the presented-memory.",
"correction": "Put the vocabulary through as an amendment to D39.42 п.1/§C2-7: new outcome fields live in the journal and signature map, not the seed schema, and an auto row drops the banknote dst. Add `not-a-term` to `proposed` so the owner sees every engine refusal in the same single OK. Introduce the area-of-use carve-out only after the leakage metric (D39.104). Move `succeed` out of package 1 until row 192 is opened by the owner."
},
{
"id": "R3",
"claim": "The design names, as the owner's own decision, each item he already settled or deferred: cross-run bank accumulation (row 130), term-class emission expansion (D39.47), the ё-fold into data (D39.64 §5 / row 14), and 'one paid book' (D39.63).",
"verdict": "PARTIAL",
"evidence": "Counted in the design (control: 5 'D39.' mentions in 10,910 characters). '130' 0, 'D39.118' 0, 'D39.50' 0, 'D39.190' 0, '192' 0, 'D39.144' 0.\n\nRow 130: NOT named. BACKLOG.md:70 lists 'накопительный мердж авто-банка … СТОП-решение владельца (двигает банк → memory_version)', gated on 'WARN второго прогона'. The code says so too, mining.go:773: 'The BOUNDARY is deliberate and is the owner's STOP: this reports, it does not accumulate'. The design puts the journal in package 1 as 'бэкенд, $0 + центы'. §5 п.1 does put the substance to the owner, but not as a decision he previously stopped, and without its gate or its cost class (volume.go:333-352 describes the edit-unit re-payment when the auto-bank grows).\n\nD39.47: named ('D39.47/ряд 440', 'Позже, по слову владельца'). Not named: D39.50 п.1, the ratified negative close of emission expansion (archive/05-decisions-D39-20-58.md:325), and D39.102 п.3 'term-класс эмиссии … не заводится'. The proposed 'частотный по паре' nomination channel runs into D39.50 п.2, 'частотного экрана НЕ существует'. The stated cause for 家族, 'режет срез top-200 ДО типа (miner_emit.go:126-129)', contradicts the corrected causality in row 440 (BACKLOG.md:361): 族 is dropped by the one-rune floor and 家族 by the type filter in `emissionEligible`.\n\nё-fold: named as the owner's queue, but §4 slots it into the re-snapshot window as work. Row 14 (BACKLOG.md:28) reads 'владелец | когда-нибудь | отдельное решение'.\n\nD39.63: the phrase 'одна платная книга' is not in D39.63. It lives in BACKLOG:95/218, research/27 and CHAPTER_STRUCTURE_DESIGN:1524; D39.63 п.3 says only 'стендовый корпус ОДНОРАЗОВЫЙ'. The ratified basis for $0 is D39.190 п.4 (log:2121): 'coldrun-v16 закрыт словом владельца «не доводить»'. For the later paid runs A (bk_ROEHZBD46ALFI43E) and B (bk_NH6275ZIRFFTLZHG) there is no such record: 'не доводить' hits only coldrun-v16 across 97 live md files, and experiments 24/25 and NEAR_TERM_PLAN hold no continuation decision. The reversal condition stated in the chapter design (CHAPTER_STRUCTURE_DESIGN:760-767) is not carried over.",
"correction": "In §5, name each item as a prior owner decision or deferral: row 130 (STOP, gate WARN, memory_version move) · D39.50/D39.102 п.3 (expansion closed negatively; a frequency screen does not exist) · row 14 (separate decision, not a window item) · row 192/D39.144 п.1 (post-reading edits not to be designed yet). Take the $0 claim from D39.190 п.4 and add the condition 'if nobody plans to continue runs A/B'. Take the 家族 cause from row 440."
},
{
"id": "R4",
"claim": "The law unit (Identity, sense, window [since, until]) is already held by the schema, and the window becomes the only tool for changing law mid-book. Successor 'from chapter N' sets until=N1, and 'главы ≤N не трогаются по построению'. At issue: whether the ordinal-window journal is a mine for the chapter re-cut.",
"verdict": "REFUTED",
"evidence": "The design never mentions chapter identity: 'chapter' 0, 'перекро' 0, 'D39.245' 0. The one mention of 'структура глав' is as a window for the editor carve-out. Meanwhile D39.245 (log:3542) and backlog row 161 move bank windows onto chapter-ID.\n\n(1) The chapter design lets the engine's own files be regenerated: CHAPTER_STRUCTURE_DESIGN:662 'движковые файлы … пере-генерируются, а не мигрируются', with the single durable exception `.mined-delta.yaml`, which 'молча указывает на чужую главу' (:183-187). The engine journal in this design is a second durable carrier holding ordinals. It is missing from the inventory 'ТАБЛИЦ, КОМАНД и ФАЙЛОВ' (:187). `since_ch` 'перекроем никем не переписывается' (:788), so after inserting a chapter before N the successor boundary silently lands on another chapter. 'Главы ≤N не трогаются' holds only until the first re-cut.\n(2) Successor windows are AUTHORED windows. The chapter design measured 'Сегодня авторских окон в дереве НОЛЬ' (:616) and treats their migration as 'проектная страховка, а не работа' (:721). The design makes that class non-empty before the re-cut, using up exactly the window the chapter design calls its opportunity.\n(3) On class `gone`, the rule 'Авторское — ДЕГРАДИРУЕТ В ОТКРЫТОЕ' (:718) turns a successor pair into two rows with the same src, different dst and overlapping windows. parseBankSeed fails loud on that (memseed.go:217 'a term has ONE rendering per chapter'). The engine-written leniency covers only wire-unfit rows (memseed.go:152), so loading the book aborts the run. Two approved rows fail at bankmaterialize.go:146.\n(4) The door verb 'с главы N' as a contract minor would take an ordinal, since BankCorrection windows are ordinals (openapi.yaml:2363 'windows move with a re-cut'). The platform already stores the order boundary as identity (platform/.../00033_order_and_price.sql:72 `ordered_through_chapter_id`).\n(5) The same UNIQUE constraint (migrate.go:203) gets migrated twice: 'UNIQUE по Identity' in package 1, and 'переезжает на chapter-id вместе с колонками' (CHAPTER_STRUCTURE_DESIGN:715). That is two stop-worlds.\n\nThe chapter design already chose ONE window: bank windows go into the re-cut (:807 row 2 '… ДА', :760 'Выбираю ОБЩЕЕ окно'). D39.245 п.1-2 says the mechanics are not ratified and the build starts by running the migration on a database copy.",
"correction": "Order the work: build the engine journal, successor and door verb inside or after the re-cut window, with windows stored as chapter-ID plus a derived ordinal column (CHAPTER_STRUCTURE_DESIGN §4.2 п.3). Or, if it has to come before, store chapter-ID from day one. Either way, add the engine journal to the migration inventory next to mined-delta. Replace 'degrade to open' for successor pairs with a loud per-term decision. Fold 'UNIQUE по Identity' into the single chapter_id UNIQUE migration. Name all this as a dependency on D39.245 and row 161."
},
{
"id": "R5a",
"claim": "The core has one Identity: universal steps plus fold steps declared as source-language data. Trad→simp and kana folding are hardwired into the core for every source, which 'ломает японский: 後 и 后 схлопываются'. A table edit costs '--resnapshot книг пары (как сегодня NormVersion, только через pack.Version())'.",
"verdict": "PARTIAL",
"evidence": "CONFIRMED: NormalizeSourceKey applies trad2simp and katakana→hiragana with no language check (norm.go:141-153), the comment scopes it 'zh/ja source surfaces' (norm.go:28), and ё→е is on the target side (norm.go:203-205).\n\nREFUTED example: 後 is NOT in the table. trad2simp.txt:13 says 'phrase-level OpenCC (one-to-many, e.g. 後→后 vs 后→后) is deliberately out of scope'; python prints 'absent 後' (control: 500 mappings). The class itself is real in the table: 葉→叶, 準→准, 範→范, 繫→系, 麵→面 are distinct kanji in Japanese.\n\nThe review question ('would it work for a pair not yet in the repo') does not fully pass:\n- A per-rune table plus a 'strip combining marks' flag cannot express 1:N folds (de ß→ss, ä→ae) or reading-dependent ko hanja→hangul, which is not a fold at all. Those need another primitive in Go.\n- A book with no pack loads silently (runner.go:415-420 'a ja book against a zh-only root just runs'), and configs/langpacks holds only ru, zh and zh-ru (16 files). Moving the kana fold into data would silently switch it off for ja books and for the ja→ru golden, which 'injects these with NO langpack' (injection.txt header).\n\nCost claim WRONG: today NormVersion is folded into memory_version (membank/memory.go:417), which counts as a bank-only move and allows a $0 re-pin (repin.go:44-46, :61-90). Through pack.Version() the table lands in `langpack_version` (snapshot.go:409), a separate field of the shared snapshot payload. classifySnapshotMove then returns moveOther: no unit in either wave is re-pinnable. Separately, both Norm and Langpack are in cutInputs (manifest.go:269-282), so the cut tag and unit ids move in both cases.",
"correction": "Keep Identity data in a plane folded into memory_version, not pack.Version(), so a table edit stays a bank-only move (the precedent is D39.118 п.3, which chose a data plane by what it moves). Make the source Identity channel declared and loud when missing, and create `ja/`. Add a string-level (1:N) fold to the primitive, and state that hanja→hangul is not a fold. Swap the example for 葉/叶 or 準/准."
},
{
"id": "R5b",
"claim": "Successor detectors are pair data, e.g. gender pronouns per chapter in `<src>/pronouns.txt` (他/她 · he/she · er/sie).",
"verdict": "REFUTED",
"evidence": "The file does not exist: configs/langpacks has 16 files, none named pronouns (find shows 0). No Go code counts pronouns: non-test Go has one hit for 他/她, a comment at checks/voice.go:316 (control: 143 non-test .go files).\n\nA word list is not enough. Measured on guzhenren-utf8.txt (control: 7,778,990 characters): 他 occurs 56,100 times as a substring, of which 其他 3,240 + 他们 6,399 + 他人 870 = 10,509 (18.7%) are not singular 'he'; 她们 is 259 of 8,509 她. Dense-script sources therefore need segmentation or an exclusion list. More to the point, a per-TERM detector has to tie each pronoun to an entity (coreference), and that is not a table.\n\nThe only automatic gender producer today is the LLM classifier (decisions.go, gender comment: 'backlog row 210 (the §2 classifier derives it from the source contexts)'). Sources with no gendered third-person pronoun (ja largely drops them, ko, tr, fi, hu, fa) leave the detector inert.",
"correction": "State the detector's parts and which of them are Go versus data: tokenisation mode by script (data via lang-script), an exclusion list (data), and pronoun-to-entity attribution (Go or LLM, not a table). Without attribution, count only proposals from divergence between new-volume drafts, and call the pronoun detector zh-family-only until a generic attribution exists."
},
{
"id": "R5c",
"claim": "The fuse 'a name cannot be thrown out' is a DETERMINISTIC miner evidence signal (`Evidence`), with surname/title/toponym patterns as pair data. With that evidence present, `not-a-term` is forbidden.",
"verdict": "REFUTED",
"evidence": "The tables are data, but under `<src>/`, not the pair (configs/langpacks/zh/surnames-single.txt, surnames-compound.txt, title-suffix.txt, topo-suffix.txt …). The channels, however, are Go and Han-only.\n- langpack.go:207-208: 'SCOPE (honest): this manifest is the zh-family NAME-MINER's morphology schema … a different source family that wants mining needs new Pack fields + parse + miner channels, not just a dir'.\n- miner_substrate.go:44 `isMinerHan`.\n- miner_patterns.go:20-22: 'several channels (surname-start, Palladius) are zh-shaped'.\n- D39.50 п.8: 'en-детектора НЕ СУЩЕСТВУЕТ (hanRuns — 0 эмиссии на латинице), ja с zh-таблицами активно неверен (0/10)'.\n- A book without a pack runs with the miner inert (runner.go:415-420).\n\nCandidates seen only by drafts (Freq 0, terminology.go:101) carry no Evidence at all. So for any non-zh-family source, and for draft-only names, the fuse does not exist, and the §5 п.2 promise 'не обеднять язык без человека' holds only for zh.",
"correction": "Say explicitly that the evidence fuse is zh-family. For a source with no detector (and for draft-only candidates), define the default: `not-a-term` is forbidden (fall back to `undecided`) or goes to the owner's list. A second source family needs a second detector (D39.50 п.8), not just a directory."
},
{
"id": "R5d",
"claim": "The outcome vocabulary lives in the prompt as data. The parser accepts only the vocabulary plus 'rendering in the target script'.",
"verdict": "PARTIAL",
"evidence": "The prompt part passes without Go edits: prompts resolve by path convention (12-go-style-notes.md §0.4; prompts/zh-ru/terminologist.md), and the precedent sentinel is an engine-neutral Go constant (terminology.go:694 `NoDst = \"⟦TM-NO-DST⟧\"`) whose instruction text lives in the pair prompt (terminologist.md:36). New outcome tokens are the same class.\n\nThe 'rendering in the target script' guard, however, fails for Latin-script targets: D39.50 п.3 says '`wellFormedLemma` пропускает латиницу', and D39.102 п.3 records 'NoDst-почти-сентинел … остаток — латиница-target'. For zh→en, a reply of 'not a term' passes a script check.",
"correction": "The parser must recognise only closed sentinel tokens (like ⟦TM-NO-DST⟧), never words, and must not rely on script to catch a refusal. State that for Latin-target pairs the script check proves nothing."
}
],
"extra_findings": [
{
"title": "Moving the Identity table into pack.Version() raises the cost of any future table edit from a $0 re-pin to full re-payment",
"evidence": "Today the table is folded via NormVersion into memory_version (backend/internal/membank/memory.go:417). repin.go:44-46 says moveBankOnly means 'the ONLY differing component is the memory version', and classifySnapshotMove (repin.go:61-90) returns moveOther on any other field. pack.Version() goes into `langpack_version` (snapshot.go:409, 477), a separate field of the shared payload of both waves.",
"impact": "The design's claim 'как сегодня NormVersion, только через pack.Version()' is wrong about cost class. On any book that is continued, every table edit re-pays both waves with no $0 re-pin, where today it is a bank-only move. The extraction itself also moves the cut tag (manifest.go:269-282), so it belongs in the re-cut window, not package 1."
},
{
"title": "The terminologist's 'not a term' verdict is invisible to the owner's single OK",
"evidence": "Section 1(f) of the design lists `proposed` as 'новое, `undecided`, `polysemous`, предложения преемников, споры', with no `not-a-term`. D39.47: 'решение о переводе принадлежит ПОДПИСИ владельца на банк КНИГИ'. Ontology 18:33-36: 'какие переводы человек действительно одобрил'.",
"impact": "The engine removes candidates from the bank by a stochastic call without the owner seeing it. That is the exact 'обеднение' the design says it prevents, and for non-zh sources the evidence fuse does not exist (see R5c)."
},
{
"title": "The design's cause for 家族 not entering the bank contradicts the corrected finding in row 440",
"evidence": "The design: 'сегодня 家族/族 режет срез top-200 ДО типа, `miner_emit.go:126-129`'. BACKLOG.md:361 (correction of 12.09, re-checked by reading the function): '`族` отсечён БЕЗУСЛОВНО как ОДНОРУННЫЙ … `家族` отсечён ТИПОМ', where `emissionEligible` checks `hasAnyType(c.Types, \"name\", \"place\", \"title\")` first.",
"impact": "The claim that the architecture 'впуск обычных существительных не закрывает и не открывает' is false for the deterministic channel: the type filter closes it by construction. A package that 'moves the cap to run events' will not let 家族 in."
}
],
"obstacles": "Not checked, or only partly:\n- CHAPTER_STRUCTURE_DESIGN.md: read §0, the §2.1-bis inventory, §2.2-bis, all of §4, pins 8-11 of §9 and §11. Not read in full: §5.2-§5.3 (stop-world count), §6, §12.\n- I could not measure whether 家族 is above or below emitRankCap. That needs running the miner on the book, and go test with books access is forbidden here. I took the causality from row 440, not from measurement.\n- Whether paid runs A/B will be continued is recorded nowhere I looked (experiments 24/25, NEAR_TERM_PLAN and a search for 'не доводить' across 97 live md files). The owner's intent cannot be verified here.\n- I did not trace what the probe found about the area-of-use carve-out's leakage. I only established that the carve-out is neither on the wire nor in the prompt, and that no later note drops it (3 hits in 13 files).\n- The list of Japanese false identities (葉/叶, 準/准, 範/范, 繫/系, 麵/面) rests on my knowledge of kanji usage, not a dictionary in the tree.\n- Design claims outside lens 2 were not re-checked: that the bank pass is bought again at signing and at every purchase, pipeline-c1.yaml:161-162, bankmaterialize.go:318-333, and terminologist resume cost.\n- No go test was run; every verdict comes from reading code and docs plus python counts.\n- Uncommitted backend changes by another session in the main checkout (including store/migrate.go and pipeline/*) were deliberately not read. If they land, schema facts and line numbers may shift."
},
"L3": {
"verdicts": [
{
"id": "M1",
"claim": "Все двери сравнивают по Identity; UNIQUE схемы (migrate.go:203,389) сегодня по сырому src; Пакет 1 включает «UNIQUE по Identity (миграция схемы)».",
"verdict": "PARTIAL",
"evidence": "ЗАМЕР. Обойдено 178 файлов БД: *.db и *.sqlite в books (find -L) плюс 2 полные базы прогонов A/B. WAL у всех 0 байт, чтение immutable полное. Имён project.db в books только 2, обе с 0 строк glossary, поэтому обход расширен до всех БД.\n- С таблицей glossary 177 файлов, с хотя бы одной строкой 150.\n- Строк glossary 13 632, alias 2 223, voice_profiles 0, address_pairs 0 (таблицы есть, напр. в базе B).\n\nНОРМАЛИЗАЦИЯ. norm.go скопирован дословно в scratch-модуль с golang.org/x/text v0.40.0. NormVersion: memnorm-v3-…+u15.0.0+xtextv0.40.0-a6c1259543b1. Самопроверка: 魯鎮→鲁镇, ハル→はる, →q, 渡<ZWSP>邊→渡边.\n\nРЕЗУЛЬТАТ ПО БАЗАМ:\n- Уникальных сырых src 229, уникальных Identity 229; src, у которых Identity≠raw: 0.\n- Конфликтов «разный сырой src, один Identity, один (book, sense, since_ch, until_ch)»: 0.\n- Контроль: сырых дублей внутри UNIQUE 0 (как требует схема); строк с ненулевым окном 1 897.\n\nИСТОЧНИКИ, из которых вид пересобирается: 90 YAML (seed, mined-delta, rejects, auto-bank, signature), 2 857 src-записей, 343 уникальные строки. Identity≠raw: 0; конфликтов внутри файлов 0; в составном наборе seed+delta+auto-bank по 10 стенд-книгам 0.\n\nЦИТАТЫ ПРОЕКТА ВЕРНЫ: migrate.go:203 `UNIQUE (book_id, src, sense, since_ch, until_ch)`, :389 то же у voice_profiles.\n\nНО «миграция схемы» — не тот инструмент:\n- migrate.go:13 `var migrations = []string{` — миграции только SQL, а Identity зависит от данных пака и в SQL не вычисляется.\n- migrate.go:358-360: пересборка этого UNIQUE «forbidden by the append-only discipline».\n- glossary.go:146-160: ReplaceBank делает DELETE+INSERT на каждом прогоне, существующих строк после следующего прогона нет.\n- Аддитивный уникальный индекс возможен (прецедент migrate.go:446 events_outbox_once, 1 из 30 CREATE), но только поверх хранимой колонки identity, т.е. «глупое хранилище» (migrate.go:169-172) получит значение, зависящее от языкового пака.\n- Выживший сырой src решает публикуемый id: TermID из сырого src (decisions.go:166-173), платформа хранит ID (platform ingest/bank.go:25-27).\n- Он же решает позицию в ORDER BY, а значит memory_version и порядок строк провода (bankmaterialize.go:25-41).",
"correction": "На существующих данных миграция пустая: 0 конфликтов в 13 632 строках и 2 857 YAML-записях. Но это корпус одной zh-книги без трад./каны/латиницы в ключах.\n\nЕдинство по Identity должно держаться отказом в сборке, а не миграцией схемы: storeOrder (bankmaterialize.go:173-185), MinedDeltaSeedCollisions (:110-113), ukey в loadAutoBank (mining.go:717-733) — переключить их ключ на Identity. Схемный индекс опционален и требует хранимой колонки.\n\nС парой «разный src, один Identity, одно окно» сборка обязана:\n- в источниках владельца (seed, mined-delta) — громкий отказ с именами обеих строк;\n- в авто-банке — сброс поздней строки с ремаркой (прецедент mining.go:730-733).\nАвтослияние недопустимо: выживший сырой src меняет публикуемый id и memory_version."
},
{
"id": "M2a",
"claim": "Терминолог только по новым Identity: одна перекупка банк-ролей на книгу — центы; Пакет 1 стоит «$0 + центы перекупки банк-ролей». Прогон B: терминология $0.083 из $0.397, второй проход $0.0338.",
"verdict": "PARTIAL",
"evidence": "ЦИФРЫ B ПОДТВЕРЖДЕНЫ. request_log базы B:\n- draft $0.12065, edit $0.19267;\n- terminology: classifier $0.04531 + terminologist $0.03803 = $0.08334 из $0.39666.\n\nТАЙМЛАЙН:\n- Прогон 1 (19:4819:58): терминология $0.04955, затем стоп; bank_stop_presented = 67.\n- Прогон 2 (hello 19:59:51): черновик реплей за $0 (6 tm_hit).\n- Терминология куплена заново 20:00:0120:01:25: classifier 3 новых хеша ($0.01556), terminologist 3 новых ($0.01823) и 1 hit f9fbb1bc38 ($0). Итого $0.03379.\n- Снапшот сменился: memory_version a47d11ec8e99→15a142bfc7d3, langpack тот же.\n\nМЕХАНИЗМ:\n- Ключ — RequestHash (render.go:352-367): SnapshotID + все сообщения. Батчи пакуются по рунам, смена состава = промах (terminologist.go:1158-1172).\n- dropBankSettled (terminologist.go:1173-1205) снимает только кандидатов, которых держит НЕ-движковая строка (unsignedEngineSurfaces, mining.go:748-760). Строки авто-банка оплачиваются каждый прогон.\n- Классификатор идёт по тому же paid (terminologist.go:405).\n\nПРИ ФИЛЬТРЕ «ТОЛЬКО НОВЫЕ» меняется состав каждого батча → первая сборка промахивается по всем чекпойнтам обеих банк-ролей → одна пере-консолидация остатка. На пилоте это ≤ ~$0.05 (первый проход B), потолок книги budget_usd 1.00 + classify_budget_usd 1.00 (pipeline-c1.yaml:173,175).\n\nСКАН АВТО-БАНКОВ: 19 файлов, 921 строка, пустой dst 3, мусорных классов dst (не-термин / цифры / маркер / без кириллицы) 0 — импорт в журнал мусора не заморозит.\n\nЧЕГО «$0 + ЦЕНТЫ» НЕ СЧИТАЕТ:\n- Словарь исходов снимает закон со строк, которые старый парсер делал законом (not-a-term, ⟦TM-NO-DST⟧). Обогащённый банк меняется → edit-волна → платная пере-редактура юнитов, где эти термины встречаются. $0-репин только для байт-равных юнитов (stagerun.go:92-113). На B редактор — $0.193 из $0.397.\n- Фильтр без append-журнала рвёт банк: writeAutoBank переписывает файл целиком из дельты (mining.go:776-790). А накопительный мердж — «СТОП-решение владельца (двигает банк → memory_version)» (BACKLOG строка 130).",
"correction": "«Одна перекупка банк-ролей — центы» верно на пилотном масштабе (≤~$0.05 на B), с потолком $2 на книгу.\n\nЦена Пакета 1 — не «$0 + центы банк-ролей». К ней добавляется оплаченная пере-редактура юнитов, где словарь исходов меняет закон. В долларах это ничтожно только в одноразовой стендовой фазе D39.63.\n\nФильтр «только новые Identity» нельзя лендить раньше append-журнала движка и слова владельца по строке 130."
},
{
"id": "M2b",
"claim": "Identity по данным пары: правка таблицы = --resnapshot книг пары «как сегодня NormVersion, только через pack.Version()».",
"verdict": "REFUTED",
"evidence": "ЭТО ДВА РАЗНЫХ КЛАССА СДВИГА.\n\nСегодня NormVersion фолдится ВНУТРЬ memory_version: memory.go:417 `h.Write([]byte(text.NormVersion() + \"\\x00\" + matchVersion + \"\\x00\"))` в ComputeVersionScopedIn, общем для базы и обогащённого банка.\n\npack.Version() фолдится ОТДЕЛЬНЫМ полем: snapshot.go:477 `LangpackVersion: r.packVersion()`. Версия = packAlgoVersion + sha256 авторских байтов (langpack.go:202-204, :323). Файл таблицы в configs/langpacks/zh её двигает.\n\nclassifySnapshotMove (repin.go:69-90): любое поле кроме memory_version → moveOther. repinnable истинен только при moveBankOnly (repin.go:283, 318).\n\nЦЕПОЧКА ПЕРЕКУПКИ:\n- stagerun.go:54-66 требует --resnapshot.\n- :92-94 доверяет chunk_status только при равном снапшоте или repinnable.\n- Иначе :536-538: `RequestHash(r.attemptRequest(st, model, snapID, …))` + GetCheckpoint; ключ со SnapshotID (render.go:365-367) → промах → платный вызов даже при байт-равном запросе.\n- Банк-роли хешируют snapshot черновика: waverun.go:179 → runBankMiningStop → runTerminologist(ctx, draftSnapshot, …) → runAttempt(…snapID…) (terminologist.go:832).",
"correction": "Через pack.Version() вынос или правка таблицы — moveOther для обеих волн и банк-ролей. Это полная перекупка каждой уже переведённой книги пары, у которой есть langpack (база A $0.419, B $0.397 — целиком).\n\n«Как сегодня NormVersion» — это moveBankOnly: $0-репин всех юнитов с неизменными байтами провода; для zh при переносе таблицы без правки это все юниты.\n\nЧтобы вынос и будущие правки стоили $0 там, где провод не изменился, хеш таблицы тождества должен ехать в memory_version (как NormVersion), а не в LangpackVersion."
},
{
"id": "M2c",
"claim": "«zh-книги переживут один пере-снапшот при выносе — в фазе «одна платная книга» это $0 (D39.63)».",
"verdict": "PARTIAL",
"evidence": "ПЛАТНЫХ КНИГ. Обойдено 205 файлов БД (books + оба стенда), request_log есть в 204, cost_usd>0 — в 162.\n- Различных book_id с cost_usd>0: 26, все — эксперименты и стенды gu-zhenren.\n- Сумма максимумов по книге ≈ $7.17: guzhenren-probe4 $2.31, bk_ROEHZBD46ALFI43E $0.419, bk_NH6275ZIRFFTLZHG $0.397 и т.д.\n\nD39.63 (05-decisions-log.md:295) п.3: «боевых книг в полёте нет, стендовый корпус ОДНОРАЗОВЫЙ (старые БД остаются читаемым $0-сырьём, перекупать их не для чего) … механика --resnapshot … про эру боевых книг после МАСШТАБА».\n\nФРАЗЫ «одна платная книга» в доках нет: 0 совпадений по md вне prompts/reports (123 файла в каждом дереве, HEAD и main). Более поздней ноты о смене фазы grep «боевых книг|книг в полёте|платной книг» не нашёл: единственный хит — сама D39.63.",
"correction": "$0 здесь значит «одноразовый стенд не перекупаем» — это 26 book_id и ≈$7 исторической траты. Это не «пере-снапшот бесплатен».\n\nПри маршруте через pack.Version первая же боевая книга пары оплачивается заново целиком (см. M2b).\n\nФормулировка «фаза одной платной книги» — пересказ, D39.63 так не говорит."
},
{
"id": "M3",
"claim": "«Повтор прогона реплеит события, а не модель (принцип 8)»; undecided — «спросить снова, когда появятся новые улики (новый том номинировал повторно)».",
"verdict": "PARTIAL",
"evidence": "КАК СЕГОДНЯ ПЕРСИСТИТСЯ СОБЫТИЕ ТЕРМИНОЛОГА:\n- checkpoints(request_hash, …, response_text, cost_usd) — схема базы B.\n- Ключ RequestHash (render.go:352-367): book, chapter, chunk_idx = порядковый номер батча, attempt, stage, role, model, temperature, reasoning, json, max_tokens, SnapshotID черновика, все сообщения.\n- Поверх — производный auto-bank.yaml, переписываемый целиком (mining.go:776-790), и сайдкары.\n- glossary_revisions (migrate.go:218-233) — журнал только approved-dst, не решений движка.\n\nКЭШ РЕПЛЕИТ ТОЛЬКО ТОЖДЕСТВЕННЫЙ ЗАПРОС. Код сам называет ловушку (terminologist.go:1158-1172): «a run whose composition differs … finds no checkpoint and buys the pass again». На B стоп→подпись→резюм реплеил 1 батч из 7 (f9fbb1bc38), 6 куплены заново.\n\nЧТО ЖУРНАЛ ПО IDENTITY ДАЁТ СВЕРХ КЭША:\n- переживает смену состава батча, сдвиг снапшота черновика и рост улик;\n- хранит не-закон (not-a-term / undecided), которого в кэше нет как факта: сегодня молчание = «не решал» (terminologist.go:341-343).\n\nПРИНЦИП 8: нумерованных принципов владельца в доках нет — 0 хитов «принцип 2/6/7/8» по 123 md каждого дерева (контроль: слово «принцип» в 46 файлах). Сверить текст нельзя.\n\nПРО UNDECIDED. Реплей детерминирован, только если (а) условие «новые улики» — чистая функция записанных входов с отпечатком в событии и (б) перезапрос пишется в журнал. Проект не задаёт ни то, ни другое.\n\nЗаявленный триггер истинен на КАЖДОЙ покупке. Стоп майнит все уже начерно переведённые куски (mining.go:80-98); volume.go:337-339: «consolidates over the chunks drafted SO FAR». Значит undecided — включая навязанный предохранителем имени при устойчивом ответе not-a-term — перезапрашивается за деньги каждой покупкой, без предела.",
"correction": "Сверх кэша чекпойнтов журнал действительно даёт реплей решений, не зависящий от состава батча. Принципу детерминизма это не противоречит при двух условиях:\n1. У события есть отпечаток улик.\n2. Перезапрос undecided стреляет только при смене этого отпечатка (а не при повторной номинации), и есть предел перезапросов на одно Identity.\n\nБез этого пункт (d) воспроизводит обратную кривую цены для подмножества undecided."
},
{
"id": "M4",
"claim": "Пакет 1 одним паком: журнал+свёртка+терминолог по новым Identity · словарь исходов+парсер+защита · Identity в ядре с таблицей в langpacks/zh, двери по Identity, UNIQUE-миграция · преемник+глагол двери (контрактный минор) · proposed · счётчик промахов.",
"verdict": "REFUTED",
"evidence": "ОБЪЁМ В GO:\n- Не-тестовых файлов с затрагиваемыми функциями (NormalizeSourceKey, load/writeAutoBank, unsignedEngineSurfaces, dropBankSettled, autoBankPath, MinedDeltaSeedCollisions, Apply/DecodeDecisions) — 17 в 6 пакетах (text, membank, miner, terminology, pipeline, store); тест-файлов 24.\n- NormalizeSourceKey: 54 не-тестовых вызова в 12 файлах (decisions.go 17, mining.go 13, terminologist.go 7, banknote.go 4, memseed.go 3, …) и 58 в тестах.\n- Пакет text объявлен «free of language policy: no pair data» (norm.go:7-8). Identity с таблицей пака = протащить пак во все эти места, включая чистую дверь decisions.go (ApplyInput пака не несёт).\n- Сверх того: internal/lang (новый канал — фиксированный srcFiles, langpack.go:206-215), данные langpacks/zh и промт терминолога, bankexport.go (proposed), cmd/tmctl/bankapply.go, store (миграции только SQL, migrate.go:13).\n\nШОВ:\n- Контракт 14 — зона оркестратора: 14-api-contract/README.md:684 «approve/decline, третьего нет».\n- Платформа пинит DecisionsVersion \"tm-bank-decisions-v1\" (platform/internal/ingest/bankdecisions.go:18), движок отвергает чужую версию (decisions.go:143-144).\n- Платформенный API пропускает только approve/decline (platform/internal/httpapi/bank.go:404).\n- Итог: третий глагол — это бамп документа плюс правка платформы. Иначе у двери нет вызывающего — второй ярус класса A (18-bank-ontology.md:65-72).\n\n«СЧЁТЧИК ПРОМАХОВ ПО НЕПОДПИСАННЫМ» УЖЕ ЕСТЬ: retrieval_state.n_unverified_shown / n_unverified_followed (migrate.go v12, ~:330-354; пишутся waverun.go:858, chunkrun.go:172; агрегат quality.go:110-116).\n\nПРОМЕЖУТОЧНЫЙ ВРЕД ПРИ НЕВЕРНОМ ДРОБЛЕНИИ:\n- фильтр без append-журнала выбрасывает строки авто-банка (mining.go:776-790);\n- словарь исходов без журнала делает not-a-term неотличимым от «не спрошен» → перезапрос каждый прогон;\n- журнал до Identity ключуется сырым src → потом его миграция;\n- Identity через pack.Version → moveOther (M2b).",
"correction": "Это 3 пака бэкенда плюс платформенный. Порядок без промежуточного вреда:\n\n1. Identity + отказы сборки по Identity + маршрут версии таблицы в memory_version. Стоит $0; на существующих данных 0 конфликтов.\n2. После слова владельца по строке 130 — строго одним паком: append-журнал движка, свёртка, фильтр «без закона в окне», словарь исходов, парсер, предохранитель и отпечаток улик. Цена: одна перекупка банк-ролей плюс пере-редактура затронутых юнитов.\n3. Преемник + DecisionsVersion v2 + минор контракта (оркестратор) + proposed-проекция. В том же окне — платформенный вызывающий глагола и читатель proposed (строка 224).\n\nСчётчик промахов — доводка существующих колонок, а не новая сущность."
},
{
"id": "M5",
"claim": "Черновик остаётся свидетелем, черновая волна не перекупается никогда; дверь пишет только дельту (Source:mined, bankmaterialize.go:318-333, decisions.go:50-57), сид не трогается никогда; главы ≤N не трогаются ⇒ $0-репин.",
"verdict": "PARTIAL",
"evidence": "ПОДТВЕРЖДЕНО ДЛЯ APPROVE/DECLINE:\n- База исключает Source:\"mined\" (bankmaterialize.go:318-333).\n- Дельта штампуется mined (bankmaterialize.go:96-98), авто-банк тоже (mining.go:734).\n- DecisionDepth=\"edit_wave\" (decisions.go:51-57); базовый фолд с доменным разделителем (memory.go:425-427,439).\n\nПРЕЕМНИК ДЛЯ СИД-ТЕРМА (подписанное ядро, имена) ЧЕРЕЗ ДЕЛЬТУ НЕВЫРАЗИМ:\n- approve по ключу сида отвергается безусловно (decisions.go:362-365): «Editing a seed term means editing glossary_seed, which moves the BASE snapshot and re-pays the draft wave».\n- Строка дельты [N,0] с другим dst поверх сид-строки [0,0] роняет прогон: ApprovedSharedKeyCollisions (memseed.go:290-304 — разные dst + windowsOverlap), фатально по bankmaterialize.go:146-148.\n- Сам проект объявляет пересечение окон одного Identity отказом линта.\n- Закрыть окно сид-строки = правка glossary_seed = перекупка черновиков глав ≥N с этим термином.\n\nIDENTITY-МИГРАЦИЯ ДВИГАЕТ БАЗОВЫЙ СНАПШОТ КАЖДОЙ КНИГИ: NormVersion фолдится и в base (memory.go:413-427, excludeMined=true). При маршруте через pack.Version это LangpackVersion (snapshot.go:477) → moveOther (repin.go:78-79) → черновая волна перекупается целиком даже при байт-равных запросах (stagerun.go:536-538).\n\nГЛАВЫ ≤N1 ПРИ ПРЕЕМНИКЕ СТРОКИ ДВИЖКА: окно на провод не выводится (editorLines: `\"- \" + src + \" → «\" + dst + \"»\"`, memory.go ~:925-935), байты не меняются → $0-репин (stagerun.go:95-111). Подтверждено.\n\nПРЕЕМНИК МЕНЯЕТ ID СТАРОЙ СТРОКИ: TermID(src, sense, since, until) (decisions.go:166-173), платформа держит ID стабильным (platform/internal/ingest/bank.go:25-27).",
"correction": "«Дверь пишет только дельту, черновик не перекупается» верно для approve/decline и для преемника строк движка и дельты.\n\nДля сид-терма нужен один из трёх путей:\n- новая семантика «дельта затеняет сид с главы N» (в коде её нет — сегодня это фатальная коллизия);\n- правка сида с перекупкой черновиков глав ≥N;\n- явный отказ преемнику сид-терма.\n\nIdentity-миграция двигает базовый снапшот всех книг; $0 она стоит только при маршруте версии в memory_version."
},
{
"id": "C1",
"claim": "Проход «куплен заново на подписи и на каждой покупке — volume.go:333-352 говорит это сам».",
"verdict": "PARTIAL",
"evidence": "volume.go:336-352 говорит о другом: каждая покупка растит авто-банк → сдвиг снапшота edit-волны → пере-оплата юнитов, где встречаются новые термины («Expect that run to need --resnapshot and to re-pay the units the new terms actually touch»). О перекупке прохода терминолога и о подписи там нет ни слова.\n\nПерекупка на подписи — факт базы B (6 из 7 батчей, $0.0338) и terminologist.go:1158-1172.",
"correction": "volume.go подтверждает «на каждой покупке» для пере-редактуры затронутых юнитов. Перекупку прохода на подписи подтверждают данные прогона B и terminologist.go:1158-1172, а не volume.go."
},
{
"id": "C2",
"claim": "pipeline-c1.yaml:161-162 называет сегодняшнюю обратную кривую цены терминологии.",
"verdict": "CONFIRMED",
"evidence": "backend/configs/pipeline-c1.yaml:161-162: «(1) бюджет КНИГО-ШИРОКИЙ и копится через покупки — книга, проданная десятью покупками, платит десять книжных консолидаций против одного бюджета»."
}
],
"extra_findings": [
{
"title": "Identity по данным пака молча ломает zh-книги без langpack",
"evidence": "langpack_root опционален: config/book.go:93-98; в pipeline 9 веток r.pack == nil / != nil. У 2 из 10 стенд-книг zh нет langpack_root: bk_LVYOO5M5ZSQEUPIK, bk_SZFCURJUQ752EXMP (у остальных 8 он есть). LangpackVersion фолдится «ONLY when a pack is present» (snapshot.go:402-409). Сегодня таблица трад→упрощ. вшита в ядро и работает для любой книги (norm.go:47-48, 147-149).",
"impact": "После выноса таблицы в configs/langpacks/zh/ у zh-книги без пака Identity теряет свёртку. Ключ 魯鎮 перестаёт матчиться с 鲁镇 — это ровно класс A4 «silently empty» (norm.go:25-27). Громкого сигнала нет: версия нормализации двинется как bank-only сдвиг. Таблицу тождества нужно грузить независимо от майнерского пака, либо сделать пак обязательным для источника, объявившего свёртку."
},
{
"title": "Глагол succeed переиндексирует строку для платформы — это не аддитивный минор",
"evidence": "TermID = sha256(src, sense, since, until) (membank/decisions.go:166-173); тот же id публикует bankexport.go:200. Платформа держит ID как «Stable across the bank being rebuilt» (platform/internal/ingest/bank.go:25-27). Преемник закрывает окно (until=N1), и id старой строки меняется.",
"impact": "После succeed решения, заметки и ссылки платформы по старому id перестают разрешаться: decisions.go резолвит решение по id. Нужна либо преемственность id (не от окна), либо явная карта old→new в квитанции двери — это меняет оценку «контрактный минор»."
}
],
"obstacles": "1. Корпус — одна zh-книга: все 229 src из БД и 343 из YAML не меняются нормализацией. Поэтому «0 конфликтов» ничего не говорит о японских и традиционно-китайских источниках, ради которых вводится Identity. Живых ja-книг и прогонов нет; утверждения о 後/后 и ハル/はる не проверял — это линзы 1/2.\n2. Нормализатор воспроизведён копией norm.go в scratch-модуле (x/text v0.40.0 из кэша модулей). Его NormVersion-хеш с версией из снапшотов движка не сверял; go test не запускал.\n3. Нумерованные принципы владельца (2/6/7/8) в доках не найдены: 0 хитов по 123 md в каждом дереве. Текст принципа 8 сверить нельзя.\n4. Платформенных баз и возможных боевых книг беты не видел — только движковые БД в books и на стендах. Актуальность фазы D39.63 на 15.09 проверена лишь грепом по журналу.\n5. Размер пере-редактуры edit-волны после смены словаря исходов не мерил: нужен платный прогон или симуляция рендера.\n6. Форма хранения журнала (таблица или файл) в проекте не задана, стоимость её миграции не оценена.\n7. Парсер исходов (terminology.go:1115-1121, miner_emit.go:261-269), editor.md и детекторы преемника не читал — вне линзы 3.\n8. Сумма «$7.17» — сумма максимумов request_log по book_id среди копий и бэкапов, не бухгалтерия."
},
"critic": {
"blockers": [
{
"title": "Преемник для имён из сида невыразим без правки сида. Центральные имена книги получают либо падение прогона, либо молча мёртвую строку.",
"ids": "F5, E-e3, M5, extra L1 «seed successor»",
"why": "Проект обещает (e) «сид не трогается никогда» и (f) «черновая волна не перекупается никогда». Код даёт три исхода. (1) Одобренный преемник поверх одобренной сид-строки роняет прогон: ApprovedSharedKeyCollisions, bankmaterialize.go:146-148; та же проверка стоит в вердикте двери, decisions.go:913-914. (2) Неподписанный преемник уступает сиду на проводе и молча не действует (memory.go:925). (3) Закрыть окно сида — это правка glossary_seed, а дверь её отклоняет (decisions.go:358-363: «moves the BASE snapshot and re-pays the draft wave»). Моя пере-проба подтвердила: для строк движка и дельты преемник работает, для сида — нет. Чтобы проект стал верен, нужно одно из трёх: новое правило «решение владельца затеняет сид с главы N» (новый писатель закона над сидом), честная перекупка черновиков глав ≥N или явный отказ преемнику для сид-терминов."
},
{
"title": "Проект строит журнал с окнами-ординалами и авторские окна преемника до перекроя глав. После первой перенарезки граница преемника молча указывает на чужую главу.",
"ids": "R4 (пере-проверен)",
"why": "CHAPTER_STRUCTURE_DESIGN.md:655-663: движковые файлы пере-генерируются, а долговечен только .mined-delta.yaml. Журнал движка из проекта — второй долговечный носитель ординалов, и в инвентаре миграции его нет. §4.2 п.4 (:717-721): «Авторское — ДЕГРАДИРУЕТ В ОТКРЫТОЕ… Сегодня авторских окон ноль… проектная страховка». Преемник делает этот класс непустым. Пара преемника, деградировавшая в открытые окна, — это один src, разные dst и пересекающиеся окна. Такая пара роняет загрузку: memseed.go:210-222 «a term has ONE rendering per chapter». Поблажка для файлов, записанных движком, покрывает только непригодные для провода строки (memseed.go:151-156), а не пересечения. §5.1 ряд 2 и §4.3 (:760-767) уже кладут окна банка в общее окно перекроя, и UNIQUE (migrate.go:203) переезжает на chapter-id. «UNIQUE по Identity» в Пакете 1 означает второй стоп-мир над тем же ключом. D39.245 ратифицировал предмет «глава = идентичность», но не механику. В проекте слов «перекрой» / «D39.245» / «chapter-ID» — 0."
},
{
"title": "Оговорка области употребления, которую проект объявляет «обязательной частью архитектуры», уже проверена пробой и опровергнута. Строка 134 закрыта без её внесения.",
"ids": "B-b3 (слабый CONFIRMED), R2 (L2: «ни одна поздняя нота её не снимает» — неверно)",
"why": "experiments/18-editor-wire-probe.md:380-392: «критерий 4 «оговорка работает» — НЕ ВЫПОЛНЕН». На репликации N=3 с оговоркой смысловой передачи 0/3, без оговорки 2/3: «Направление обратно гипотезе». archive/PROGRESS-2026-09-04-07-bank-law.md:50-53: «строка 134: рендер снят… Оговорка области НЕ внесена (опровергнута §A.4)»; приёмка экспа 18 — D39.108. На этой оговорке проект держит механизм «имя против обычного слова» (方正) и исход polysemous. Без неё у полисемии внутри окна нет безопасного пути на провод: закон плюс «СТРОГО» ломает не-терминное употребление, not-a-term теряет консистентность имени. Проект обязан либо назвать другой механизм, либо оставить такие поверхности человеку."
},
{
"title": "Проект включает в Пакет 1 то, что владелец остановил или велел не проектировать, и не называет это его словом.",
"ids": "R3, R2, M2a",
"why": "Накопление авто-банка между прогонами — ряд 130 (BACKLOG.md:70): «СТОП-решение владельца (двигает банк → memory_version)», гейт — WARN второго прогона; mining.go:773: «this reports, it does not accumulate». Глагол succeed — это пост-ридинговая правка, а D39.144 п.1 (05-decisions-log.md:579): «точную механику пост-ридинговых правок… НЕ проектировать до полигонных итогов». Частотный канал номинации упирается в D39.50 п.2 («частотного экрана НЕ существует») и D39.102 п.3. «Одна платная книга» и $0 опираются на D39.63, хотя основание — D39.190 п.4 с условием «если книгу не продолжат», а статус прогонов A/B нигде не записан. §5 проекта задаёт владельцу вопросы так, будто он решает впервые."
},
{
"title": "Тождество ключа через pack.Version() меняет класс цены: сегодняшний $0-репин превращается в полную перекупку обеих волн. Книги без langpack молча теряют свёртку.",
"ids": "F3, R5a, M2b, M2c, extra L3 «Identity по данным пака»",
"why": "Сегодня NormVersion фолдится внутрь memory_version (memory.go:417), и это moveBankOnly. pack.Version() — это отдельное поле langpack_version (snapshot.go:409,477), то есть moveOther (repin.go:61-90): ни один юнит не пере-пинивается. langpack_root необязателен (config/book.go:93-98), у 2 из 10 стенд-книг его нет. Формулировка проекта «как сегодня NormVersion, только через pack.Version()» ложна именно в деньгах. Исправление: хеш таблицы тождества фолдить в memory_version, а таблицу грузить независимо от майнерского пака."
},
{
"title": "Пакет 1 одним паком непостроим без промежуточного вреда. Его фильтр «терминолог только по новым Identity» глушит споры, а их замена лежит в Пакете 2.",
"ids": "M4, extra L1 «366 споров», extra L3 «TermID», F-f1",
"why": "NormalizeSourceKey — 54 не-тестовых вызова в 12 файлах; дверь decisions.go — чистая функция без пака. Платформа пинит DecisionsVersion v1 (platform/internal/ingest/bankdecisions.go:18) и пропускает только approve/decline (httpapi/bank.go:404). Контракт 14 — зона оркестратора. TermID зависит от окна (decisions.go:166-173), поэтому succeed меняет id старой строки, а платформа держит id стабильным: это не аддитивный минор. Фильтр без детектора споров снимает метки противоречия с банком у 366 кандидатов корпуса (terminologist.go:1127-1134). Без append-журнала writeAutoBank переписывает файл и выбрасывает строки (mining.go:776-790). Реалистично 3 бэкенд-пака плюс платформенный, в порядке M4."
}
],
"gaps": [
{
"title": "Консистентность длинной книги против денег: новый закон задним числом. Проект не выбирает и не спрашивает.",
"why": "Строки, найденные только черновиками (62 из 64 в авто-банке B), эмитятся с SinceCh: 0 (mining.go:612), майненые — с главы первого появления. Новый закон, решённый на покупке 2, ложится на уже отредактированные главы покупки 1. Моя проба (scratchpad/verify-design-completeness/crit_successor_test.go; настоящие ru-тексты, порядок стора, перенос из предыдущего куска): строка 方正 [0,0] меняет провод глав 1 и 4 (wireSame=false). При тесном бюджете она ВЫТЕСНЯЕТ из старого куска уже действующую строку 方源, потому что при равном ранге порядок — байтовый по src (memory.go:649-676, glossary.go:225). volume.go:336-352 сам называет это перекупкой юнитов на каждой покупке. Цифры проекта «цена падает с томами» и «том 2 не меняет закон тома 1» учитывают только терминолога, а редактор в B — $0.193 из $0.397. Выбор нужен явный: пере-редактировать уже прочитанные главы ради одной формы (деньги, читатель видит правку) или закон с границы покупки (ранние главы остаются разнобойными)."
},
{
"title": "Детерминизм и спойлеры: «улики по всей книге» дают терминологу будущие главы, а закон потом действует с since=0.",
"why": "spoilerBlocked (memory.go:679-689) описан как «a fact the current chapter must not know yet». Режим пола hidden (memory.go:946-947, D19.3) — уже построенный механизм «до раскрытия», то есть частный случай преемника, и проект с ним не связан. Сегодня стоп читает только уже начерненные куски (volume.go:337-339). Выборка по всей книге превращает раскрытие из главы 500 в закон главы 1."
},
{
"title": "Род в преемнике: дверь род не принимает, производитель рода — классификатор, а не терминолог.",
"why": "decisions.go:84-88: «It is still NOT ACCEPTED… correcting one stays a seed edit». Род на авто-строки ставит классификатор (mining.go:172 attachClassifiedGender), а в словаре исходов проекта род стоит в law(dst, gender, conf) терминолога. Смена роли-производителя, правка промта, приём рода дверью и контракт в Пакете 1 не перечислены."
},
{
"title": "Детерминизм перезапросов: у undecided нет отпечатка улик и предела, у not-a-term нет пути назад.",
"why": "Триггер «новый том номинировал повторно» истинен на каждой покупке: стоп заново майнит все начерненные куски (mining.go:80-98). Значит перезапросы undecided платные и без предела (M3). not-a-term записывается навсегда, в proposed его нет (дизайн §1(f)), и если слово позже окажется именем, вернуть его некому."
},
{
"title": "«Без новых стадий» и «деньги видимы»: проект вводит платные и новые проходы без сметы.",
"why": "«N=2-согласие на спорных (центы)» — новый класс платного вызова без замера. Детектор рода по местоимениям — не таблица: нужна кореференция, а 18,7% вхождений 他 — не единственное «он» (R5b). Цена Пакета 1 «$0 + центы» не включает пере-редактуру юнитов, где словарь исходов снимает закон (M2a), перекупку через pack.Version (M2b) и миграцию существующих авто-банков в журнал."
},
{
"title": "«Язык — данные» держится только для zh.",
"why": "Предохранитель по улике майнера — это zh-каналы на Go (langpack.go:207-208, miner_substrate.go:44 isMinerHan). Детерминированная улика есть у 2 из 66 кандидатов B (D-d3). Таблица «руна→руна» не выражает свёртки 1:N (ß→ss), а ханча→хангыль вообще не свёртка. Если перенести кана-свёртку в данные, она молча выключится у ja-книг без пака. Проверка «рендеринг в целевом письме» ничего не ловит для латинских целей (R5d)."
},
{
"title": "Проблема «нет читателя» между пакетами усиливается.",
"why": "Пакет 1 делает долговечными решения «не закон», а читатель proposed у платформы — это Пакет 2. Сегодня у секции 0 читателей (ряд 224; json:\"proposed в platform/ — 0 при 209 go-файлах). В промежутке движок принимает необратимые решения, которых никто не видит."
},
{
"title": "Алиасы и голоса вне модели тождества и преемника.",
"why": "Алиасы — такие же матчащиеся поверхности (memory.go:328-330), в корпусе 2 223 строки. Дверь переносит их при промоушене (decisions.go:78-80), но проект не говорит, что делает с ними Identity, UNIQUE и преемник. voice_profiles несут тот же ключ (src, sense, since, until) (migrate.go:389), а цель продукта — консистентные голоса; в проекте они не упомянуты."
},
{
"title": "Рост банка упирается в бюджет, который уже недосчитывает строки.",
"why": "Проект называет бюджет 800 предохранителем роста. Ряд 131 (BACKLOG.md:71): бюджет не считает маркер и гендер-ноты, превышение ~4060%. Вытеснение внутри равного ранга — по байтовому порядку src, то есть случайное для смысла. Сегодня n_evicted = 0 в A и B (контроль: колонка есть, по 6 строк retrieval_state), так что риск перспективный, но для накопительного банка книги в тысячу глав он не посчитан."
}
],
"owner_questions": [
"Банк памяти и перенарезка глав трогают одни и те же окна «с главы N». Строить журнал решений и «преемника» после перенарезки (или вместе с ней, с окнами по идентичности главы), чтобы не мигрировать дважды? И собираетесь ли вы продолжать книги прогонов A/B: от этого зависит, бесплатен ли пере-снапшот.",
"Вы остановили накопление авто-банка между прогонами до второго прогона (ряд 130) и просили не проектировать правки после прочтения до итогов полигона (D39.144). Проект строится на обоих. Снимаете ли вы оба запрета сейчас, или журнал и глагол «с главы N» ждут?",
"Если термин решён на второй покупке, а встречался уже в первой: пере-редактировать уже готовые главы ради одной формы на всю книгу (это деньги, и читатель увидит правку в прочитанном) или вводить закон только с границы покупки, оставив ранние главы как есть?",
"Если имя из вашего сида (списка имён, заданного вручную) нужно сменить с главы N: согласны ли вы, что это пере-оплачивает черновики глав от N с этим именем? Или нужно новое правило «правка владельца перекрывает сид с главы N»?",
"Может ли движок сам решить «это не термин» и не показать вам? Защита имени сегодня срабатывает примерно у 3% кандидатов и только для китайского, а проверенная пробой «оговорка области» не сработала. Или каждое «не термин» должно стоять в списке на ваш один ОК?"
],
"weakest": [
{
"id": "B-b3 (L1 CONFIRMED)",
"why": "Буквально верно: в editor.md и injection.txt оговорки нет. Но подменён предмет: «обещанное и забытое» на деле проверено и опровергнуто. L2 (R2) ошибся, написав, что ни одна поздняя нота её не снимает.",
"recheck": "Пере-проверил, НЕ устоял как довод проекта. Скан 7 промтов zh-ru и injection.txt: 0 формулировок оговорки (контроль: editor.md 42 строки, injection.txt 14). experiments/18-editor-wire-probe.md:380-392: «критерий 4 «оговорка работает» — НЕ ВЫПОЛНЕН»; N=3: с оговоркой по смыслу 0/3, без неё 2/3. archive/PROGRESS-2026-09-04-07-bank-law.md:53: «Оговорка области НЕ внесена (опровергнута §A.4)». Оговорка: сама проба мала (одно окно, N=3, текст знаком моделям по претрейну), но она принята D39.108 и закрыла строку 134."
},
{
"id": "F5 (L1 PARTIAL, несущий $0-репин)",
"why": "Проба L1 шла с пустыми InjectionTexts: байты директивы рода не сравнивались. Не было также порядка стора, переноса из предыдущего куска и тесного бюджета.",
"recheck": "Пере-проверил исполнением (go test -overlay, дерево 0 изменений до и после). Условия: настоящие ru-тексты (контроль HasData=true, «муж. — мужские родовые формы»), ORDER BY стора, перенос из предыдущего куска, строка с hidden, бюджеты 800 и 12. Главы 1 и 4: wireSame=true при сдвинутой версии. Главы 5 и 8: провод сменился на «Старый Фан (жен.)». Юниты редактуры не пересекают главы (chunker.go:386-405). УСТОЯЛ для строк движка и дельты. Для сида по-прежнему ломается (блокер 1). Попутно: новый закон с since=0 меняет провод глав 1 и 4 и при бюджете 12 вытесняет строку 方源."
},
{
"id": "R4 (L2 REFUTED проекта)",
"why": "Несущий аргумент опирался на цитаты дизайна глав, который сам принят без сходимости механики.",
"recheck": "Пере-читал CHAPTER_STRUCTURE_DESIGN.md:655-663, :717-721, :760-767 и тело D39.245. Правило пересечений в memseed.go:210-222 не прикрыто поблажкой engineWritten (она только для непригодных для провода строк, :151-156). УСТОЯЛ. Уточнение: «деградация в открытое» — нератифицированная механика, но какой бы она ни стала, журнал движка с ординалами и авторские окна преемника в её инвентарь обязаны войти."
},
{
"id": "M1 (L3 PARTIAL, 0 конфликтов миграции)",
"why": "Ноль получен на корпусе, который физически не может дать конфликт.",
"recheck": "Пере-проверил другим прибором: предфильтр классов символов вместо копии нормализатора. Обойдено 178 файлов БД, с glossary 177; строк glossary 13 632, алиасов 2 223, поверхностей стопа 14 884, уникальных строк 229. Строк, которые Identity изменила бы, — 0; конфликтов — 0. Контроль ёмкости: в единственном zh-исходнике guzhenren-utf8.txt 7 778 990 рун (Han 6 205 702), рун-ключей trad2simp 0, катаканы 0 (таблица: 500 пар). ФАКТ УСТОЯЛ, КАК УЛИКА ПУСТ: про японские и традиционные источники, ради которых вводится Identity, ноль ничего не говорит."
},
{
"id": "F7 (L1 CONFIRMED)",
"why": "Путь в коде доказан пробой парсера, но «сегодня «не термин» и «90» становятся законом» в оплаченных данных не наблюдалось: 0 из 129 строк ответов в B и 0 из 137 в A.",
"recheck": "Не пере-проверял. Дефект латентный, поэтому приоритет «словаря исходов» как лечения наблюдаемой болезни преувеличен."
},
{
"id": "F8 (L1 CONFIRMED)",
"why": "«Центы» за выборку по всей книге — оценка из разбиения токенов прогона B, не замер. Доля контекстов в промпте не выделена: тексты запросов в БД не хранятся.",
"recheck": "Не пере-проверял; нужен замер до стройки."
}
],
"verdict_on_design": "Как направление для владельца проект годен: закон как долговечное решение с окнами, закрытый словарь исходов вместо догадки-закона, тождество источника как данные. В нынешнем виде нести его нельзя. До этого проект обязан: встать в очередь относительно перекроя глав (окна по идентичности главы); назвать запреты владельца (ряд 130, D39.144 п.1, D39.50) вопросами; убрать опровергнутую оговорку области как несущий механизм и показывать not-a-term владельцу; вести версию таблицы через memory_version, а не pack.Version; явно решить преемника для сида и новый закон задним числом; разрезать Пакет 1 на три упорядоченных пака плюс платформенный.",
"obstacles": "Все три линзы дали вердикты (null нет). Что не проверил и где слеп:\n- Список «восьми проблем» и нумерация принципов (2/6/7/8, «позиция 5») пришли из промта оркестратора старшему коллеге, в репозитории их нет: 0 хитов «без новых стадий|закон в точке банка|не обеднять язык» при 123 md-файлах. Пробелы я сверял с принципами в формулировке моего задания.\n- Преемника и новый закон проверил на уровне membank (Select + RenderEditorConstraintBlock), а не сквозь renderedContentHashes и rowsResumeFree с настоящими шаблонами. То, что content_hash зависит только от отрендеренных сообщений, выведено чтением repin.go:128-160.\n- Вытеснение бюджетом в накопительном банке — перспективный риск: в A и B n_evicted = 0 при всего 6 строках retrieval_state на книгу.\n- Платформенные базы и возможные боевые книги беты не видел. Будут ли продолжать A/B, нигде не записано.\n- Опровержение оговорки опирается на пробу 18 (одно окно, N=3, текст, знакомый моделям по претрейну). Её вес ограничен, но она ратифицирована приёмкой (D39.108) и закрыла строку 134.\n- Не пере-проверял: японские пары кандзи (F2), счёт местоимений (R5b), денежные цифры и число файлов (M2, M4), парсер отказа (F7).\n- Незакоммиченную работу другой сессии в основном дереве (migrate.go, stagerun.go, quality.go и др.) не читал: после её лендинга номера строк могут сдвинуться.\n- Мои пробы запускались через go test -overlay из scratchpad/verify-design-completeness; git status дерева /home/ubuntu/tm-verify-1509 — 0 до и после."
}
}