Ratify D39.51 banknote discipline and answer-language design, sanction build items one through seven, cold mini-run configuration, land polygon report correction

This commit is contained in:
Claude (backend session) 2026-07-26 21:42:08 +03:00
parent fb5e873157
commit 7692457e9a
4 changed files with 585 additions and 2 deletions

File diff suppressed because one or more lines are too long

View file

@ -1154,3 +1154,7 @@ API-529-долг закрыт: 8-осевой refute-by-default воркфлоу
**Ратифицировано фактами:** (1) **расширение `emitRankCap`/фильтра типа НЕ строится**: 66 из 74 поверхностей банкноты лежат вне варианта C по ЧАСТОТЕ (82% ниже emitMinFreq на срезе — вне кандидатного алфавита), а на хвосте расширенной эмиссии роль ВЫДУМЫВАЕТ dst 7782% стабильно (сентинел ⟦TM-NO-DST⟧ различает «переводимо», не «терм», и нестабилен даже там — размах 0.375); (2) **носитель покрытия — банкнота** (5/6 кандидатов, класс term с dst), но она невоспроизводима (Жаккар 0.0770.40 между прогонами одних глав) и эмитирует 《咏梅》/咏梅 как разные строки — лечения, выдержавшие проверку: нормализация книжных кавычек + агрегация по прогонам; **частотного экрана НЕ существует** (§5.1: 少年 1149 против 光阴之河 15 — порога нет; полигон сам снял свою рекомендацию); (3) **дыра языка ответа**: ⟦TM-CANON⟧ — де-факто якорь ЦЕЛЕВОГО ЯЗЫКА; без него 22/40 английских строк в одном прогоне из трёх (8×120 тоже течёт), и конвейер их банкует (`wellFormedLemma` пропускает латиницу) — закрыть ДО любого расширения канала; (4) **kwic-дефолт 3×40 ДОСТАТОЧЕН** (ни одна точка сетки не отличима, 0×0 — лучшая; развилка «подача×контекст» неразличима), НО `kwic_width` в РУНАХ = утечка пары в общий слой (4384 морфемы zh против обрубка фразы en) → пар-данные; (5) **蛊/转 подписаны и не доезжают ни одним каналом** (`SignificantLen=1`) — правка ДАННЫХ сида (`allow_short: true`); (6) **условие судьи-экрана (фолбэк D39.46) наступило**, но экран платный за кандидата — дизайн обязан посчитать, где ставить и что снимается бесплатно; (7) **холодный старт с пустым банком — не мерил никто**, и именно там цена любого утверждения о покрытии (кандидат — совместить с мини-прогоном D39.40); (8) §6: **en-детектора НЕ СУЩЕСТВУЕТ** (`hanRuns` — 0 эмиссии на латинице), ja с zh-таблицами активно неверен (0/10) — второй книге нужен второй ДЕТЕКТОР, эксперименты на Кристоффе преждевременны; прототип каналов — `mine_nonhan.py` пакета-7.
**Напряжение с D39.46 «контексты — главный рычаг» названо, НЕ отменено:** три различия постановки (лёгкая выборка · якорь в боевой сборке · нет строки drafts:); пере-замер на трудной выборке пакета-7 с боевой сборкой — кандидат, до него kwic не трогать. **Дефект нарезки пакета-7** (фильтр по номеру 节 при перезапуске нумерации в томах): абсолютные recall фазы A занижены втрое (0.089→0.255 на непрерывном срезе), структурный вывод (term кратно слабейший) УСТОЯЛ. Приёмка: 8/8 CONFIRMED, ревью-шапка с нитами на отчёте `POLYGON_PREMEASURE_2026-07-26.md`; деньги $0.11313 из потолка $0.30.
## D39.51 — Дизайн «дисциплина банкноты + дыра языка ответа» РАТИФИЦИРОВАН, стройка санкционирована (владелец: «идём», 26.07): языковой экран на ВЫХОДЕ + счётчик OffLanguage + фильтр свёртки банкноты + нормализация кавычек в свёртке (Q1) + агрегация из чекпойнтов (A1) + kwic_width в пар-данные (файл zh-ru НЕ шипуем) + doc-хвост. Судья ОТЛОЖЕН; мини-прогон D39.40 — В ХОЛОДНОЙ конфигурации; allow_short — на нём (26.07, оркестратор №8). ✅
**Отчёт:** `docs/archive/reports/PACK20_BANKNOTE_DESIGN_2026-07-26.md` (ревью-шапка). Принципиальные решения: (1) **гарантия языка — на выходе, не на входе** (предикат offLanguage по `*unicode.RangeTable` через конфиг гейта `target_script`, обязателен при enabled, вне снапшота, $0; направление отказа объявлено: отвергнутый законный латинизм дешевле англо-канона); (2) **кавычки — в свёртке (Q1), не в парсере/NormalizeSourceKey** (Q2/Q3 платят волной за косметику; Q1 $0 и ретроактивен; NormVersion не двигается — тест-страж); (3) **агрегация банкноты — производное из чекпойнтов (A1), без новых таблиц**; инвариант «прогон без новых черновиков даёт байт-идентичную карту» — пином; предложения отвергнутых попыток входят (решение: предложение = улика, не канон); (4) **судья-с-декоем НЕ строится** (≈$0.025/книга — цена не аргумент; его положительное решение = решение о переводе за владельца, против D39.47; популяция неизвестна до холодного старта; сначала P1); (5) **три расхождения сессии с записками оркестратора приняты** (allow_short = базовая версия = перепокупка книги → на холодный прогон; kwic-файл zh-ru не шипуем; судья отложен). **Замеры полигону:** пример формата в промпте (≈$0.009, критерий до прогона: 0 не-целевых строк ×3 против 22/40) · P1 consistency ($0: Σchunks-предложивших / chunks-содержащих; порог засчитывается при ≥80% термов и ≥60% мусора; арм-без-фактора = голая частота обязана провалиться; положительный исход = право на ПОРЯДОК, не на выброс строк) · приёмка предиката на живом сырье 147 вызовов (живо, подтверждено: ровно 27 строк §3.4 и ни одной сверх). **Мини-прогон D39.40 = холодная конфигурация** (пустой сид, банкнота+терминолог ON, --verify-bank, ОТДЕЛЬНАЯ БД) — пять ответов за ≤$0.23: банкнота без цензуры · совместный recall (замер 1 состоится) · язык при пустом якоре (счётчик OffLanguage как прибор) · побочки allow_short · второй сэмпл для агрегации. Приёмка стройки: мутации M-Я1/M-Я2 + регресс 16 живых строк + пин общности (латинская цель переворачивает вердикты) + пин инварианта агрегации + сырьё-приёмка.

View file

@ -0,0 +1,576 @@
# Пак-20, продолжение: ДИЗАЙН по замерам пакета-8 — дисциплина банкноты и дыра языка ответа
**База:** D39.50 (пакет-8 принят, 8/8 CONFIRMED), D39.47/48, `POLYGON_PREMEASURE_2026-07-26.md`.
**Зона:** `backend/` (эта сессия — только ЧТЕНИЕ; ни одной строки продового кода не изменено).
**Статус:** фаза ДИЗАЙНА. Деньги сессии **$0** (ни одного платного вызова). Сессия НЕ коммитит.
**Дальше:** СТОП → ратификация владельца → стройка.
> **Ревью-шапка (оркестратор №8, 26.07): ДИЗАЙН РАТИФИЦИРОВАН, D39.51 — стройка пп.17 §8 санкционирована.**
> Спот-проверка оркестратора: `AllowShort` в хеше строки банка (memory.go:368) и место эхо-гейта
> (terminology.go:882-885) — сошлись; четыре опоры сессии проверены ЕЮ исполнением, полная проверка —
> мутациями приёмки стройки. Все три расхождения с записками оркестратора ПРИНЯТЫ как правильные:
> `allow_short` = перепокупка книги (не «строка данных») → на холодный прогон; пар-файл kwic для zh-ru
> не шипуем (значение = фолбэк, LangpackVersion не трогаем); судью не строить — не из-за денег, а
> потому что его положительное решение отнимает подпись у владельца (принцип D39.47) + популяция
> неизвестна до холодного старта. Ответ на §10 п.6: сырьё 147 вызовов ЖИВО
> (скретчпад полигон-сессии, raw2/raw3/raw3b + raw_anchor побайтовая копия; верификатор приёмки D39.50
> пересчитывал по нему деньги) — приёмка предиката §2.5 п.5 на настоящем сырье ОБЯЗАТЕЛЬНА.
> Дефолты по решениям §10 (вето владельца одной строкой): судья ОТЛОЖЕН до P1 + холодного старта ·
> мини-прогон D39.40 идёт В ХОЛОДНОЙ конфигурации (отдельная БД — операционное условие §6 в силе) ·
> `allow_short` для 蛊/转 — данными на нём же. Полигону — два замера (§8 пп.89, ≈$0.009 + $0).
Посылка «расширить эмиссию майнера» закрыта замером отрицательно, и хорошо, что до стройки: §2 моей
дельты (`PACK20_DELTA_2026-07-26.md`) устарела целиком, кроме одного — оси «что банкуется без подписи
≠ что кладётся на подпись». Она пережила пакет-8 и стала в этом дизайне несущей.
---
## §0 Эхо-блок (что я услышал, ДО работы)
1. Не проектировать: подъём `emitRankCap`, снятие фильтра типа, частотный экран для банкноты, подъём
kwic-дефолтов. Всё четыре закрыты замером — механизмом, а не порогом.
2. Предмет: (1) дыра языка ответа — закрыть ДО любого расширения канала; (2) дисциплина банкноты
(кавычки + агрегация по прогонам; семантический отбор — судья или владелец, посчитать деньгами);
(3) `kwic_width` из Go-константы в пар-данные, значение НЕ менять; (4) `allow_short` — упомянуть;
(5) холодный старт — не мерить самому, предложить, как его снимет мини-прогон.
3. `NormalizeSourceKey` — ключевая нормализация; оси объявить явно.
4. Нормы D39.46/47: арм без фактора · сравнение на расходящихся позициях · декой · критик полноты на
фазу · право сказать «этого делать не надо».
5. Алгоритмы не подгонять под язык/книгу. Ревью допустимо, скоуп-крип — нет.
6. Отчёт → СТОП. Судья в прод-провод — только с отдельной санкцией.
---
## §1 Синк-таблица: источник ↔ код ↔ вердикт
Вердикт «подтверждаю исполнением» означает, что я выполнил проверку в этой сессии (временный тест,
удалён сразу после снятия чисел); «подтверждаю чтением» — что грунтовал по `file:line`, но не запускал.
| # | Утверждение полигона | Адрес в коде | Вердикт |
|---|---|---|---|
| 1 | Роль отвечает по-английски, и конвейер это банкует (§3.4) | [terminology.go:842-892](backend/internal/terminology/terminology.go#L842-L892) `ParseReply`, [:576-588](backend/internal/terminology/terminology.go#L576-L588) `wellFormedLemma` | **Подтверждаю ИСПОЛНЕНИЕМ.** Боевой `ParseReply` на строках `修行→cultivation`, `元海→Primordial Sea`, `转→realm`, `是为→is`: **принято 5 из 5, bad_lines = 0**. Ни один счётчик не шевельнулся |
| 2 | Якорь ⟦TM-CANON⟧ — де-факто якорь целевого ЯЗЫКА, и он пуст на холодном старте | [terminologist.go:307-316](backend/internal/pipeline/terminologist.go#L307-L316), [terminology.go:714-765](backend/internal/terminology/terminology.go#L714-L765) `CanonFor` | **Подтверждаю чтением.** `CanonFor` берёт только APPROVED-строки; их ноль до первой подписи. Пустой якорь → `renderPairs` даёт `""` → сообщение не отправляется вовсе |
| 3 | Эхо-гейт не ловит английский | [terminology.go:885](backend/internal/terminology/terminology.go#L885) | **Подтверждаю чтением.** Проверка `normalize(dst) == key` ловит эхо ИСХОДНИКА; английское слово исходником не является |
| 4 | `《咏梅》` и `咏梅` — две разные строки карты подписи | [norm.go:136-156](backend/internal/text/norm.go#L136-L156), [banknote.go:288](backend/internal/pipeline/banknote.go#L288) | **Подтверждаю ИСПОЛНЕНИЕМ.** `key(《咏梅》)="《咏梅》"` против `key(咏梅)="咏梅"`, не равны. NFKC книжные кавычки не снимает — они не совместимостные |
| 5 | `蛊`/`转` подписаны и не доезжают ни одним каналом | [memory.go:276](backend/internal/membank/memory.go#L276), [norm.go:252-263](backend/internal/text/norm.go#L252-L263), [miner_emit.go:27,33](backend/internal/miner/miner_emit.go#L27) | **Подтверждаю ИСПОЛНЕНИЕМ.** `SignificantLen(蛊)=1`, `SignificantLen(转)=1`, `SignificantLen(蛊师)=2` — ровно порог `minKeyLenHan=2` |
| 6 | Банкнота — носитель покрытия (5/6 кандидатов) | [mining.go:87-120](backend/internal/pipeline/mining.go#L87-L120), [terminology.go:128-214](backend/internal/terminology/terminology.go#L128-L214) `Merge` | **Подтверждаю чтением и УТОЧНЯЮ:** в живом слое это ещё и означает, что ~84% кандидатов несут строку `drafts:` — то есть целевой язык уже стоит в запросе. Замер §3 его снял («черновых вариантов нет») и потому меряет риск языка в его ХУДШЕЙ точке. Дыру это не отменяет (см. §2.0) |
| 7 | Частотного экрана для банкноты не существует (§5.1) | [miner_emit.go:27](backend/internal/miner/miner_emit.go#L27) `emitMinFreq` | **Подтверждаю и присоединяюсь.** Порога, разделяющего `少年` (1149) и `光阴之河` (15), нет. Своё предложение частотного порога полигон снял сам — это образцовая работа, и повторять её я не буду |
| 8 | kwic-дефолт 3×40 достаточен | [terminologist.go:57-61](backend/internal/pipeline/terminologist.go#L57-L61) | **Принимаю вердикт, оспариваю его перенос.** Достаточность измерена на ЛЁГКОЙ выборке без строки `drafts:` (§3.3 сам называет три различия). Практический вывод — «поднимать нечем» — от этого не страдает, и я не предлагаю поднимать |
| 9 | `kwic_width` в рунах — утечка пары в общий слой (§6.3) | [terminology.go:331-346](backend/internal/terminology/terminology.go#L331-L346) `window` | **Подтверждаю чтением.** Окно режется в рунах, константа одна на все пары |
| 10 | Эмиссия банкноты — лотерея прогона (Жаккар 0.0770.40) | [glossary.go:258-299](backend/internal/store/glossary.go#L258-L299) (PK `book_id,chapter,chunk_idx`), [banknote.go:330-379](backend/internal/pipeline/banknote.go#L330-L379) | **Подтверждаю чтением и УТОЧНЯЮ механизм:** внутри ОДНОЙ базы повтор невозможен — чекпойнт реиграет тот же текст, и `bankProposalsJSON` пересчитывается из него ([stagerun.go:415](backend/internal/pipeline/stagerun.go#L415)). Разъезд возникает только когда черновики РЕАЛЬНО перекупаются; тогда `UPSERT` по PK **затирает** прошлые предложения — объединение теряется |
| 11 | Роль выдумывает dst 7782% на расширенном хвосте | — | **Принимаю; предмета для кода нет** (расширение не строится) |
| 12 | Условие фолбэка «судья-с-декоем» наступило | — | **Принимаю.** Считаю деньгами в §3.4 |
**Побочная находка ревью (не скоуп-крип, а мой собственный хвост D39.47):** в
[pipeline.go:255-261](backend/internal/config/pipeline.go#L255-L261) doc-комментарий `TerminologyGate`
всё ещё обещает роли «its genre glossary». Механизма нет с D39.47 — осталась строка документации.
Правка на одну строку, кладу в стройку этого пакета, отдельного решения не прошу.
---
## §2 Дыра языка ответа (главный предмет)
### §2.0 Что именно сломано — точная формулировка
Не «модель иногда отвечает по-английски». Сломано то, что **у конвейера нет ни одного места, где
проверяется, что ответ роли написан на целевом языке**, и все три существующих стража пропускают
латиницу по своим законным причинам:
- `wellFormedLemma` пропускает — латиница легитимна для латинских имён;
- эхо-гейт молчит — ответ не равен исходнику;
- `CanonConflicts` молчит — конфликтовать не с чем, якорь пуст.
Дальше строка едет по штатному маршруту: `ParseReply``attachConsolidatedDst`
([terminologist.go:384-396](backend/internal/pipeline/terminologist.go#L384-L396)) → `status: draft`
авто-банк → инъекция редактора с ⟨проверить⟩. То есть **`修行 → cultivation` становится рабочим каноном
книги**, и единственное, что стоит между ним и текстом, — глаз владельца на стопе, которого в авто-режиме
нет по определению.
Важное уточнение к §3.4 полигона, в обе стороны:
- **риск в бою МЕНЬШЕ измеренного:** в живом батче ~84% кандидатов (65 из 78 на срезе §1.3) несут строку
`drafts:` с русскими вариантами, а замер их снял ради чистоты фактора. Целевой язык в запросе обычно
присутствует;
- **но точка отказа ровно та, где этой страховки нет:** холодный старт (якорь пуст) + кандидат от майнера
без черновых вариантов (11 из 78) = батч, в котором целевого языка нет НИГДЕ, кроме промпта. Промпт
написан по-русски целиком — и §3.4 показывает, что этого не хватило.
Отсюда конструктивное требование: гарантия языка **не должна зависеть от содержимого запроса**. Всё, что
живёт во входе (якорь, черновики, примеры в промпте), — это снижение вероятности; гарантия может стоять
только на ВЫХОДЕ.
### §2.1 Вариант А (рекомендуемый): детерминированный экран целевого письма на выходе
**Предикат.** Строка ответа не является переводом, если в ней есть буквы и **ни одна** из них не
принадлежит письму цели. Числа, пунктуация, дефисы — не буквы и не участвуют.
```
offLanguage(dst, script) := (letters(dst) > 0) && (hits(dst, script) == 0)
```
**Общность.** Письмо приезжает в `terminology` ЗНАЧЕНИЕМ (`*unicode.RangeTable`), как уже приезжает
`Conformance func(dst, typ) float64` ([terminology.go:351-361](backend/internal/terminology/terminology.go#L351-L361)).
Ни имени языка, ни имени письма в пакете нет. Таблица резолвится по ИМЕНИ из `unicode.Scripts`
стандартная таблица Go, покрывающая любое письмо. Ревью-вопрос «заработает ли на паре, которой в репо
ещё нет, без правки Go?» — **да**: новая пара объявляет своё письмо строкой конфига.
**Проверено исполнением на реальных строках отчёта** (не на выдуманных фикстурах):
| Множество | Строки | Результат |
|---|---|---|
| Английские ответы §3.4/§2.6 | `cultivation` · `Primordial Sea` · `Rank 1` · `Grade C` · `talent` · `Wine Bug` · `realm` · `Qingmao Mountain` · `Gu Yue` · `is` | **10 из 10 помечены** |
| Подписанные владельцем и живые русские | `культивация` · `море истинной ци` · `гу-мастер` · `Фан Юань` · `гу` · `талант` · `винный червь` · `старейшина` · `Гуюэ` · `апертура` · `третья гу` · `гу Силы` · `Река времени` · `9` · `«Ода сливе»` · `Фан Юань` | **16 из 16 прошли** |
| Тот же предикат при латинской цели | `культивация` / `cultivation` | роли меняются местами, Go не трогается |
Ложных срабатываний нет ни одного, включая пограничные: `гу` (2 буквы), `9` (букв нет — не трогаем),
кавычки и двойной пробел.
**Где стоит.** Ровно там, где уже стоит эхо-проверка — [terminology.go:885](backend/internal/terminology/terminology.go#L885),
и по той же логике: «ответ, который есть исходник, — не перевод» ↔ «ответ, который написан не на языке
цели, — не перевод». Строка считается плохой (`bad`), термин остаётся неконсолидированным
(`status: auto`, инертен). Ни одна выдумка в банк не попадает.
**Направление отказа названо явно.** Предикат может отвергнуть ЗАКОННЫЙ латинский рендеринг (пара, где
владелец хочет сохранить «Wi-Fi» латиницей). Цена ошибки — потерянное предложение, которое владелец
подпишет руками; цена противоположной ошибки — англоязычная строка в каноне книги. Ошибаться в эту
сторону — решение, а не недосмотр.
**Наблюдаемость (обязательна, иначе экран сам становится тихой деградацией).** Отдельный счётчик
`OffLanguage` в `terminologyResult` ([terminologist.go:65-81](backend/internal/pipeline/terminologist.go#L65-L81))
НЕ слияние с `BadLines`: «модель ответила не на том языке» и «модель сломала формат» требуют разных
действий. Батч, где доля не-целевых строк ≥ ½, логируется `Warn` с именами строк: это ровно сигнатура
срыва §3.4 (22 из 40), и она должна быть видна оператору в тот же момент, а не в отчёте через сутки.
**Тот же предикат — на втором входе.** Черновая банкнота тоже несёт dst в банк
([banknote.go:330-379](backend/internal/pipeline/banknote.go#L330-L379)), и `是为 → is` в §2.6 показывает,
что канал не иммунен. Фильтр ставится в СВЁРТКЕ (`bankObservedByKey`) и на записи
(`bankProposalsJSON`), а не в парсере — почему именно так, см. оси в §3.1.
**Чего экран НЕ делает** (границы, чтобы его не переоценили): он не ловит смешанную строку
(`cultivation (культивация)` пройдёт — кириллица есть), не ловит правильный язык с неправильным
смыслом, и не улучшает качество ни на одном термине. Он закрывает ровно один класс: язык.
### §2.2 Вариант Б: строгая форма предиката (доля, а не наличие)
`offLanguage := hits/letters < 0.5`. Ловит `cultivation (культивация)` и пояснения в скобках, которые
промпт запрещает, а `wellFormedLemma` пропускает (скобки не в списке `[]{}<>|`). **Не рекомендую в этом
паке:** это уже не «язык», а «формат ответа», отдельный класс с собственной ценой ложных срабатываний
(законные скобки в титуле). Называю как открытую опцию, чтобы не подавать её потом как новую находку.
### §2.3 Вариант В (вход, дополняющий, НЕ заменяющий): пример формата в промпте
§3.4 показывает не «модель не знает языка», а «модель повторяет письмо примеров, которые видит».
Тогда самое дешёвое воздействие на вход — не якорь, а **строка примера формата в промпте роли**,
написанная на целевом письме. Это ДАННЫЕ (`prompts/zh-ru/terminologist.md`), Go не трогается, снапшот
не двигается, цена — перепокупка только батчей терминолога.
**Но это гипотеза, а не замер, и я не предлагаю её строить вслепую.** Она проверяется на уже
построенном харнессе полигона одним армом: их же «3×40 без якоря» ×3 повтора, плюс строка примера.
Критерий назвать ДО прогона (предлагаю: 0 не-целевых строк в 3 прогонах против 22/40 в контрольном).
Цена по их же таблице §4 — **≈$0.009**.
Порядок важен: вариант А строится независимо от исхода В. Если В сработает, А почти никогда не будет
срабатывать — и это правильное состояние: страховка, которая молчит.
### §2.4 Деньги и оси варианта А
| | |
|---|---|
| **Деньги стройки** | $0 (детерминированный предикат, ни одного вызова) |
| **Деньги в проде** | $0 навсегда |
| **Снапшот движка** | НЕ двигается: предикат читает ОТВЕТ, байты запроса не меняются; `gates.terminology` в снапшот не фолдится ([pipeline.go:263-268](backend/internal/config/pipeline.go#L263-L268)) |
| **Батчи терминолога** | НЕ перекупаются (адрес батча — байты запроса) |
| **Книга без англоязычных строк в банке** | путь байт-в-байт прежний, не перебилливается |
| **Книга, у которой такая строка в авто-банке ЕСТЬ** | строка исчезает → обогащённая версия банка сдвигается → **редакторская волна перебилливается ОДИН раз**. Это штатный путь «банк изменился», а не `--resnapshot` движка |
| **Конфиг** | новый ключ `gates.terminology.target_script` (имя письма). Обязателен при `enabled: true`, проверяется на ЗАГРУЗКЕ по идиоме [pipeline.go:986-1003](backend/internal/config/pipeline.go#L986-L1003) — гейт, который не может отработать, отвергается до денег |
**Почему конфиг, а не пар-данные langpack.** Файл в `configs/langpacks/<пара>/` фолдится в
`Pack.Version()``LangpackVersion`**обе волны** ([snapshot.go:428](backend/internal/pipeline/snapshot.go#L428))
→ полная перепокупка книги. Платить перепокупкой черновой волны за объявление письма — несоразмерно.
`gates.terminology` не фолдится вообще, поэтому там объявление стоит $0. Общность при этом сохранена:
значение — данные, не Go.
**Альтернатива, которую я рассмотрел и отверг:** выводить письмо цели из уже загруженных пар-данных
(`palladius.txt` целиком на кириллице). Соблазнительно — ноль новых ключей, — но это скрытая связь:
файл написан для транскрипции, и правка в нём молча меняла бы поведение языкового экрана. Явное
объявление дороже на одну строку конфига и честнее.
### §2.5 Приёмка (как это доказывается, а не заявляется)
1. **Мутация M-Я1:** снять предикат → тест на реальных строках §3.4 обязан покраснеть.
2. **Мутация M-Я2:** ослабить до «есть хоть одна буква цели ИЛИ строка короче N» → красное на `is`.
3. **Регресс ложных срабатываний:** 16 живых русских строк (включая подписанные владельцем) — зелёные.
4. **Пин общности:** тот же предикат с латинской целью меняет вердикты местами — доказывает, что в Go
не осело знание о паре.
5. **$0-верификация на настоящем сырье:** полигон сохранил все 147 вызовов пакета-8. Если сырьё живо —
прогнать предикат по ним целиком: он обязан пометить ровно 27 известных строк §3.4 и ни одной сверх.
Это лучшая приёмка, чем любая фикстура, и стоит $0. **Запрос к полигону:** доступно ли сырьё.
---
## §3 Дисциплина банкноты
### §3.0 Что здесь на самом деле лечится
Банкнота даёт 5/6 кандидатов и умеет класс `term` с dst — и она же невоспроизводима. Три дефекта
разной природы, и их нельзя лечить одним средством:
| Дефект | Природа | Лечение | Цена |
|---|---|---|---|
| `《咏梅》``咏梅` | нормализация | детерминированное, $0 | §3.1 |
| объединение предложений теряется при перекупке черновиков | долговечность | детерминированное, $0 | §3.2 |
| 48% предложений — разовые фразы («звук дождя») | СЕМАНТИКА | бесплатного нет — замер, потом судья или владелец | §3.33.4 |
**Почему мусор в банке вреден — назову прямо, потому что от этого зависит, сколько за отбор не жалко
платить.** Строка `雨声 → «звук дождя»` со статусом `draft` попадает в инъекцию редактора с пометкой
⟨проверить⟩. Она (а) ест бюджет инъекции и вытесняет настоящие термы (`n_evicted` это уже считает),
(б) засоряет карту подписи владельца и (в) — главное — **предписывает редактору переводить обычное
слово ОДНИМ фиксированным способом**. Последнее прямо противоречит цели проекта: механизм, созданный
против расхождения терминов, применённый к не-термину, производит ровно ту деревянность, ради победы
над которой всё строится.
### §3.1 Кавычки: нормализация обрамляющей парной пунктуации
**Правило (пар-слепое по построению).** Снять с ключа обрамление, если ПЕРВАЯ руна — открывающая
(`unicode.Ps` `unicode.Pi` ASCII `"` `'`), а ПОСЛЕДНЯЯ — закрывающая (`unicode.Pe` `unicode.Pf`
те же ASCII); повторять, пока снимается; остановиться, если остаток пуст. Требование парности не даёт
покалечить `O'Brien` или строку, у которой кавычка только с одной стороны. Работает для `《》`, `「」`,
`“”`, `«»`, `""` — то есть для пар, которых в репо ещё нет, без правки Go.
**Где стоит — и это главная ось.**
| Место | Что чинит | Цена |
|---|---|---|
| **(Q1) свёртка `bankObservedByKey` + запись `bankProposalsJSON`** ← **рекомендую** | ключи сходятся, 4 строки карты подписи становятся 2, ретроактивно для уже оплаченных прогонов | **$0.** `retrieval_state` не wire-load-bearing ([migrate.go:316-327](backend/internal/store/migrate.go#L316-L327)); снапшот не двигается |
| (Q2) парсер `parseBanknote` | то же | `bankParserVersion` фолдится ([snapshot.go:113-130](backend/internal/pipeline/snapshot.go#L113-L130)) → `--resnapshot`**перепокупка всей черновой волны** каждой книги с включённым каналом |
| (Q3) `text.NormalizeSourceKey` | то же + совпадение с ключами банка памяти | `NormVersion``memoryVersion`**обе волны**; плюс смена семантики матчинга для всей памяти |
Q2 и Q3 платят волной за косметику. Q1 даёт тот же результат бесплатно и вдобавок лечит УЖЕ записанные
строки, потому что свёртка перечитывает сохранённый JSON.
**Дом функции:** `internal/text` рядом с прочими чистыми предикатами, с явным комментарием, что она
**не входит** в нормализационный артефакт и `NormVersion()` не двигает, + тест-страж на неизменность
`NormVersion()`. (Альтернатива — `internal/terminology`; выбрал `text`, потому что предикат нужен обоим
потребителям и не содержит терминологической логики.)
**Измеренный эффект:** Жаккар между двумя прогонами одних глав 0.077 → 0.400 (§1.4), минус 3 дубля из
67 строк карты подписи (4.5%). Честно: **это не лечение precision, это лечение тождества.**
### §3.2 Агрегация по прогонам: объединение вместо затирания
**Что ломается сегодня.** `retrieval_state` имеет PK `(book_id, chapter, chunk_idx)` и UPSERT
([glossary.go:258-299](backend/internal/store/glossary.go#L258-L299)). Пока черновики резюмятся из
чекпойнтов — предложения воспроизводятся байт-в-байт. Как только черновик РЕАЛЬНО перекуплен (снапшот
сдвинулся), новая строка затирает старую, и предложения прошлого сэмпла исчезают навсегда, хотя
чекпойнт с их текстом лежит в базе.
**Вариант A1 (рекомендуемый): объединение выводится из ЧЕКПОЙНТОВ, нового долговечного состояния нет.**
Свёртка читает не текущие `retrieval_state`, а все чекпойнты книги переводческой роли, и разбирает из
них блоки ⟦TM-BANK-v1⟧ тем же кодом, которым это уже делается на резюме
([stagerun.go:415](backend/internal/pipeline/stagerun.go#L415)). Нужен один новый store-запрос
(`checkpoints JOIN jobs ON job_id WHERE book_id=? AND role=?`), с исключением производных
(`finish_reason='banknote_export'` — там блок уже срезан) и с тем же гейтом `finish=="stop"`.
- **Плюс:** ноль новых таблиц и миграций; свойство «пересчитывается каждый прогон, самолечится»
сохранено (это норма этого кода: производное лучше хранимого); покрытие **монотонно** — поверхность,
однажды предложенная, больше не теряется.
- **Минус, называю прямо:** в объединение войдут и предложения ОТВЕРГНУТЫХ попыток (эхо, обрыв). Считаю
это приемлемым и объявляю решением: предложение — это улика, а не канон; владелец всё равно подписывает.
Альтернатива (только победившие попытки) требует читать `chunk_status.final_hash`, который сам
перезаписывается — то есть лечит симптом тем же механизмом, который сломан.
**Вариант A2:** отдельная таблица `banknote_proposal` (append-only). Отвергаю: долговечное состояние,
не выводимое из чекпойнтов, миграция, и первый же баг записи становится неисправимым — против дисциплины
всего этого хранилища.
**Обязательство, которое я обязан доказать тестом, а не заявить.** [mining.go:108-120](backend/internal/pipeline/mining.go#L108-L120)
прямо предупреждает: если дельта отличается между прогоном 1 и прогоном 2, сдвигается редакторская
волна и книга перебилливается ни за что. Объединение это ужесточает. Инвариант: **прогон, который не
купил ни одного нового черновика, обязан дать байт-идентичную карту подписи.** Он держится по
построению (множество чекпойнтов не изменилось → объединение не изменилось), но именно поэтому его
нужно запинить тестом, а не оставить рассуждением.
**Что агрегация НЕ даёт — и это важно для ожиданий.** Новых поверхностей она не приносит, пока никто не
купил второй сэмпл черновика. Второй сэмпл — это вторая черновая волна, самая дорогая часть конвейера;
рекомендовать её ради покрытия я не буду. Агрегация покупает **немонотонную потерю → монотонность**, и
только её. Единственный бесплатный второй сэмпл, который у нас будет естественным образом, — мини-прогон
холодного старта (§6).
**Оси:** объединение может ДОБАВИТЬ строк → авто-банк вырастет → обогащённая версия банка сдвинется →
редакторская волна перебиллится один раз, и только у книги, у которой черновики покупались более одного
раза. У книги с одним сэмплом — байт-в-байт прежнее поведение.
### §3.3 Семантический отбор: сначала $0-замер, и он может закрыть вопрос
Прежде чем предлагать платный экран, обязан назвать бесплатный сигнал, который никто не мерил, —
и мерить его я предлагаю ДО стройки, потому что если он работает, платить не надо вовсе.
**Сигнал.** Частота в тексте отвергнута замером. Но в свёртке уже лежит другое число: `Chunks` — в
скольких ЧАНКАХ переводчик независимо предложил эту поверхность
([banknote.go:351-356](backend/internal/pipeline/banknote.go#L351-L356)). Это не частота в тексте, а
**повторяемость решения**: сколько раз независимый чтец, глядя на разные куски, счёл строку достойной
объявления термином. Ставлю гипотезу на отношение
```
consistency := Σ chunks(предложившие поверхность) / (число чанков, где поверхность ВСТРЕЧАЕТСЯ)
```
`少年` встречается в множестве чанков и предложен один раз → отношение близко к нулю. `光阴之河`
встречается в двух и предложен в одном → 0.5. Это ортогонально частоте — то есть не повторение
опровергнутого порога.
**Замер P1 ($0, полигон, из уже сохранённых артефактов).** На 74 размеченных поверхностях посчитать
`consistency` и проверить, разделяет ли она «настоящий терм книги» и «обычное слово». Критерий назвать
ДО счёта (предлагаю: разделение засчитывается, только если существует порог, оставляющий ≥80% настоящих
термов и убирающий ≥60% мусора; иначе — «не разделяет»). Арм без фактора обязателен: та же проверка для
голой частоты — она обязана провалиться (это уже известно из §5.1 и служит калибровкой методики).
**Заранее говорю, чего этот замер не может.** Разметка «настоящий/мусор» — один разметчик и один вкус;
на границе (`家主` «глава клана» против `中年` «средний возраст») она сама шатается. Поэтому положительный
исход P1 даёт право строить БЕСПЛАТНЫЙ ранжирующий сигнал (сортировка карты подписи, порядок подачи),
но **не право автоматически выбрасывать строки**. Выбрасывание — это решение о содержимом банка книги,
и по духу D39.47 оно принадлежит подписи владельца, а не порогу в Go.
### §3.4 Судья-с-декоем: где ставить и сколько стоит (расчёт, а не оценка)
Условие фолбэка D39.46 наступило. Считаю деньгами.
**Популяция.** 67 поверхностей на 10 главах → ~6.7 на главу. Для книги в 100 глав — **≈670**
(линейная экстраполяция, то есть ВЕРХНЯЯ граница: словарь книги насыщается, и доля новых поверхностей к
концу падает).
**Цена одного кандидата.** Улика судьи — та же, что у терминолога: исходник + контексты + предложенный
dst. Мой собственный замер на боевой сборке: **223 токена на кандидата** при kwic 3×40. Выход вердикта
≈10 токенов. Цены `deepseek-v4-flash` из [models.yaml](backend/configs/models.yaml): $0.14/$0.28 за 1M.
| Статья | Токенов | $ |
|---|---|---|
| Вход 670 × 223 | 149 410 | 0.0209 |
| Выход 670 × 10 | 6 700 | 0.0019 |
| +10% строк-декоев (калибровка, не удвоение) | | 0.0023 |
| **Один проход, книга 100 глав** | | **≈$0.025** |
| Три повтора (если понадобится устойчивость) | | ≈$0.075 |
С кэшем (в пакете-8 75% входа пришло из кэша) реальная цифра ниже. Против цели ~$0.85/ранобэ — **≈3%**.
**Вывод по деньгам, который я обязан сказать честно:** экран дёшев, и цена — НЕ аргумент против него.
Но и экономии он не даёт: терминолог на тех же 670 кандидатах стоит примерно столько же, так что
«отсечём мусор, чтобы сэкономить на роли» — самообман (экономия ≈$0.011). Оправдание у экрана одно —
качество банка (§3.0), и решать его судьбу надо в этих терминах.
**Где ставить — три позиции.**
| Позиция | Что защищает | Оценка |
|---|---|---|
| **J1: перед терминологом**, на banknote-only кандидатах | И авто-банк, И карту подписи, И вход роли — одним экраном выше по течению | **предпочтительная**, если экран вообще ставится |
| J2: после консолидации | только банк; роль уже потратилась | хуже J1 без выигрыша |
| J3: только на карте подписи владельца | лист подписи; авто-банк остаётся замусоренным | не закрывает §3.0(в) — редактор всё равно получает мусор |
**И тем не менее — рекомендация: сейчас НЕ строить.** Три причины, по убыванию:
(1) $0-замер P1 может снять предмет целиком; (2) новый ПЛАТНЫЙ вызов в прод-проводе требует отдельной
санкции по этой же записке; (3) главная неизмеренная цифра — холодный старт (§6) — меняет саму
популяцию, которую судья бы фильтровал: неизвестно даже, 670 их или 2000. Ставить фильтр на популяцию
неизвестного размера — это проектировать по догадке ровно там, где пакет-8 только что показал цену
догадки.
---
## §4 `kwic_width` → пар-данные (значение не меняется, меняется место жительства)
**Дефект (§6.3):** 40 рун = 4384 морфемы на zh (две реплики диалога) и обрубок фразы посреди
предложения на en. Одна константа означает разное — классическая утечка пары в общий слой.
**Вариант A (рекомендуемый): необязательный пар-файл + инженерный фолбэк.**
`configs/langpacks/<пара>/terminology.txt` с парами `ключ значение` (`kwic_width 40`, при нужде
`kwic_per_term 3`), читается идиомой `readOptional` — **ровно как `heading.txt` и `dc-checkers.txt`**
([langpack.go:196-225](backend/internal/lang/langpack.go#L196-L225)): отсутствует → фича инертна и
`Version()` побайтно стабилен; присутствует → байты фолдятся, правка = громкий `--resnapshot`.
Приоритет резолвинга в одном месте (`terminologyOpts`,
[terminologist.go:99-112](backend/internal/pipeline/terminologist.go#L99-L112)): **явный конфиг книги →
пар-данные → инженерный фолбэк**.
**Для zh-ru файл НЕ шипуем.** Измеренное значение совпадает с фолбэком, значит файл не купил бы ничего,
а `Pack.Version()` сдвинулся бы → `LangpackVersion`**обе волны** → полная перепокупка книги. Платить
перепокупкой за переезд константы, которая не меняется, нельзя. Механизм при этом построен: пара en-ru
шипует свой файл со своим значением, и на ней ничего перекупать не надо — там ещё ничего не куплено.
**Вариант Б: мерить окно не в рунах, а в предложениях источника** (`sentence-terminator.txt` в паке
уже есть, [langpack.go:139-151](backend/internal/lang/langpack.go#L139-L151)). Единица становится
пар-осмысленной по построению. **Не рекомендую сейчас:** это меняет БАЙТЫ контекстов → перекупку батчей
терминолога, а сетка §3.1 показала, что ни одна форма подачи не отличима от другой. Менять то, что
измеренно не влияет, — трата без гипотезы. Называю как опцию на случай, если пере-замер на трудной
выборке (§7) покажет, что контексты всё-таки решают.
**Оси варианта A:** zh-ru — ничего не двигается, ноль перепокупок. Новая пара с файлом — её собственные
батчи терминолога, волн нет. Go-фолбэк остаётся, но перестаёт быть единственным источником истины, и
ревью-вопрос «заработает ли на паре, которой нет в репо» получает ответ «да, дропом файла».
---
## §5 `allow_short` для `蛊`/`转` — данные, но НЕ бесплатные
Механизм подтверждён исполнением: `SignificantLen(蛊)=1` при пороге 2, обход — `allow_short: true` в
строке сида, Go не трогается ([memory.go:276](backend/internal/membank/memory.go#L276)). Пока согласен
с полигоном. Дальше — три вещи, которых в §1.5 нет, и они меняют вывод «просто добавь строку».
1. **Это перепокупка ВСЕЙ книги.** `AllowShort` фолдится в хеш строки банка
([memory.go:368](backend/internal/membank/memory.go#L368)), а `蛊` — строка с `source: seed`, то есть
входит в БАЗОВУЮ версию банка ([snapshot.go:264-271](backend/internal/pipeline/snapshot.go#L264-L271)).
Базовая версия — это снапшот ЧЕРНОВОЙ волны. Правка данных, но цена — заново купленная книга.
2. **Ключ из одного иероглифа сидит внутри своих же составных.** `蛊` — подстрока `蛊师`, `蛊虫`,
`力量蛊`. Он начнёт срабатывать в каждом таком чанке, а разрешение вложенности уйдёт на
trust-gate/суппрессор (`n_trust_gated_suppress`) — механизм есть, но нагрузка на него станет
систематической, а не редкой.
3. **Пост-чек становится обязательством на почти каждый чанк.** 188 вхождений на 10 главах: E1 начнёт
требовать «гу» в выходе почти везде, и часть требований будет ложной — «гу» законно живёт внутри
«гу-мастера», а совпадение считается по нормализованной целевой форме.
**Рекомендация:** строку сида добавить — но **на холодном мини-прогоне** (§6), где книга покупается
заново по другой причине и цена пункта 1 равна нулю, а пункты 23 сразу измеряются готовыми счётчиками
(`n_evicted`, `n_trust_gated_suppress`, `n_postcheck_miss`). Отдельного «просто добавим строку» я не
рекомендую: это выглядит как правка данных, а стоит как волна.
---
## §6 Холодный старт: как его снимает мини-прогон (предложение, не исполнение)
**Почему это несущая цифра.** Замер 1 не состоялся именно потому, что 49 из 51 термов сида были
инъектированы, а промпт переводчика просит «новые, которых нет в глоссарии». Значит **всё, что мы знаем
о банкноте, измерено на канале, которому заткнули рот**. Сколько она предлагает, когда её не глушит
инъекция, не знает никто, — а от этого зависит и размер популяции для §3.4, и оценка покрытия, и веса
§C2-3.
**Предложение: мини-прогон D39.40 исполняется в холодной конфигурации.** Не отдельный замер — тот же
прогон, только с пустым банком.
- **Что меняется:** книга стенда конфигурируется без сида (`seed_glossary` пуст) — правка ДАННЫХ; канал
банкноты ON, терминолог ON, `--verify-bank` для полного стоп-стола.
- **Почему это не искусственно, а НАОБОРОТ:** новая книга в проде приходит именно так — без единой
подписанной строки. Холодный прогон репрезентативнее того, что мы гоняли до сих пор.
- **Цена:** одна черновая волна на 10 главах. Мини-прогон 26.07 стоил **$0.23**; пустой банк убирает
инъекцию (входные токены падают), так что это верхняя граница.
**Что он закрывает за одни деньги — пять вопросов сразу:**
| Вопрос | Чем измеряется | Кем ждался |
|---|---|---|
| Сколько предлагает банкнота без цензуры инъекцией | число поверхностей, `n_banknote_lines` | §1.7, D39.50 п.7 |
| Совместный recall майнер банкнота против подписанного сида | сид не инъектирован → знаменатель чист, замер 1 состоится | §1.2 (не состоялся) |
| Течёт ли язык ответа вживую при ПУСТОМ якоре | счётчик `OffLanguage` из §2.1 — сам по себе измерительный прибор, $0 | §3.4 |
| Побочки `allow_short` на `蛊`/`转` | `n_evicted`, `n_trust_gated_suppress`, `n_postcheck_miss` | §5 |
| Второй сэмпл черновика для агрегации (§3.2) | объединение с существующим прогоном тех же глав | §1.4 |
**Условие, которое делает эти цифры сравнимыми:** прогон должен идти в ОТДЕЛЬНОЙ базе, а не поверх
существующей (иначе чекпойнты реиграют старые черновики и «холодного» ничего не будет). Это операционная
деталь, но без неё замер молча превратится в резюм.
---
## §7 Чего я НЕ проектирую и почему (границы)
1. **Расширение эмиссии** — закрыто D39.50. Ни `emitRankCap`, ни фильтр типа, ни `feed_cap` в этом
дизайне не трогаются.
2. **Частотный экран банкноты** — опровергнут §5.1, повторять не буду.
3. **Подъём kwic-дефолтов** — оснований нет (§3.2 полигона); переезд места жительства ≠ смена значения.
4. **Судья в прод-проводе** — посчитан (§3.4), но не проектируется до отдельной санкции и до P1.
5. **Пере-замер «контексты — главный рычаг» на трудной выборке** — напряжение D39.46 ↔ пакет-8 названо
полигоном честно; это работа полигона, а не бэкенда, и до неё kwic трогать нечем в обе стороны.
6. **Второй детектор для латиницы (§6)** — отдельный пак, отдельное решение; в этом дизайне только
отмечаю, что он не блокирует ни один пункт выше.
7. **Строгая форма языкового предиката (§2.2)**, **окно в предложениях (§4Б)** — названы как опции,
строить не предлагаю.
---
## §8 Сводка: что строить, что мерить, что решать
| # | Пункт | Действие | Деньги стройки | Деньги прода | Двигает ли снапшот |
|---|---|---|---|---|---|
| 1 | Языковой экран на выходе (§2.1) | **СТРОИТЬ** | $0 | $0 | нет; редакторская волна ОДИН раз только у книги с англо-строкой в банке |
| 2 | Счётчик `OffLanguage` + громкий лог батча (§2.1) | **СТРОИТЬ** вместе с 1 | $0 | $0 | нет |
| 3 | Языковой фильтр на свёртке банкноты (§2.1) | **СТРОИТЬ** | $0 | $0 | нет |
| 4 | Нормализация обрамляющих кавычек, Q1 (§3.1) | **СТРОИТЬ** | $0 | $0 | нет |
| 5 | Агрегация из чекпойнтов, A1 (§3.2) | **СТРОИТЬ** + пин инвариант «резюм даёт байт-идентичную карту» | $0 | $0 | только у книги с >1 покупкой черновиков, один раз |
| 6 | `kwic_width` в пар-данные, вариант A (§4) | **СТРОИТЬ**, файл для zh-ru НЕ шипуем | $0 | $0 | нет |
| 7 | Doc-хвост `TerminologyGate` про жанровый словарь (§1) | **СТРОИТЬ** (одна строка) | $0 | $0 | нет |
| 8 | Пример формата в промпте роли (§2.3) | **МЕРИТЬ**, ≈$0.009 (полигон) | — | $0 | перекупка только батчей терминолога |
| 9 | `consistency` как отбор банкноты (§3.3) | **МЕРИТЬ P1, $0** (полигон) | — | — | — |
| 10 | Судья-с-декоем J1 (§3.4) | **НЕ строить**; ≈$0.025/книга, ждёт P1 + холодный старт + санкцию | — | ≈$0.025 | — |
| 11 | `allow_short` для `蛊`/`转` (§5) | **ДАННЫЕ**, только на холодном прогоне | — | — | базовая версия банка = вся книга |
| 12 | Холодный старт (§6) | **исполнить мини-прогон в холодной конфигурации** | ≤$0.23 | — | новая база |
**Итого денег на всё до стройки: ≈$0.009 (полигон) + $0 (P1).** Стройка пунктов 17 — $0 и без единого
вызова провайдера. Прод после стройки не дорожает ни на цент.
---
## §9 Адверсариальный селф-ревью
Бью по собственному дизайну; то, что устояло, оставлено, то, что не устояло, вычеркнуто выше.
1. **«Языковой экран — костыль; лечить надо промпт».** Частично верно, и потому вариант В (§2.3) в
дизайне есть. Но вход лечится вероятностно, а якорь пуст ровно там, где страшнее всего. Экран стоит
$0 и молчит, когда всё хорошо. Оставляю оба, с явным порядком: гарантия не зависит от исхода замера.
2. **«Предикат сломает пару, где цель латиница, а исходник — тоже».** Проверил исполнением: предикат
слеп к исходнику и меняет вердикты вместе с целью. Для en→ru он даже ЦЕННЕЕ (§6.2 полигона: срыв в
английский на английском исходнике глазом неотличим от работы).
3. **«≥1 буква цели — слишком слабо, `cultivation (культивация)` пройдёт».** Верно. Назвал как вариант
Б и не рекомендую в этом паке: это другой класс (формат, не язык) со своей ценой ложных
срабатываний. Не прячу.
4. **«Агрегация из чекпойнтов затащит мусор отвергнутых попыток».** Верно, объявил решением. Отвергнутый
черновик — плохой ТЕКСТ; его банкнота от этого не становится неправдой, а подписи всё равно нет.
5. **«Агрегация нарушает детерминизм между прогонами и перебиллит волну».** Самое серьёзное возражение;
[mining.go:108-112](backend/internal/pipeline/mining.go#L108-L112) предупреждает об этом прямым
текстом. Ответ: множество чекпойнтов меняется ТОЛЬКО когда куплены новые черновики, то есть волна и
так перекупалась. Но рассуждения мало — поэтому инвариант вынесен в обязательство приёмки (§3.2).
6. **«Кавычки надо чинить в `NormalizeSourceKey`, там место нормализации».** Соблазнительно и почти
правильно семантически. Цена — перепокупка обеих волн у каждой книги плюс смена семантики матчинга
всего банка памяти ради 4.5% строк. Q1 даёт тот же результат за $0 и ретроактивно. Оси выписаны.
7. **«`consistency` — это переодетая частота, которую только что опровергли».** Проверяю честно:
частота считает вхождения в текст, `consistency` — долю чанков, где независимый чтец ОБЪЯВИЛ строку
термином, к числу чанков, где строка вообще есть. `少年` частотен и почти никогда не объявлен —
именно на этой паре они и расходятся. Но гипотеза остаётся гипотезой: поэтому P1, с критерием ДО
счёта и с армом-без-фактора (голая частота обязана провалиться).
8. **«Судья дешёв — значит стройте».** Считаю это ловушкой. Дёшев он на популяции, размер которой
неизвестен (холодный старт не мерен), и его положительное решение — это выбрасывание строк из банка
книги, то есть та самая «решение о переводе за владельца», от которой D39.47 только что отказался.
Экран должен предлагать порядок, а не отнимать право подписи.
9. **«Вся эта фактура — одна книга, одна пара, одна модель».** Да. Поэтому единственные вещи, которые я
предлагаю строить, — детерминированные, пар-слепые и $0: они не опираются на числа этой книги.
Всё, что опирается (пороги, отбор), отправлено в замер.
10. **Против собственной дельты.** §2 моего же отчёта от 26.07 предлагала расширение эмиссии и считала
его деньги. Замер закрыл посылку. Не переношу оттуда ничего, кроме оси «что банкуется без подписи ≠
что кладётся на подпись» — она пережила проверку и стоит под §3.0.
### Критик полноты (норма D39.46, на эту фазу)
| Проверка | Итог |
|---|---|
| Каждый пункт записки покрыт | 5 из 5 (§2 язык · §3 дисциплина · §4 kwic_width · §5 allow_short · §6 холодный старт) |
| Синк-таблица «источник ↔ код ↔ вердикт» | §1, 12 строк + найденный doc-хвост |
| Проверено исполнением, а не чтением | 4 опоры: приём английского боевым `ParseReply`, расхождение ключей кавычек, `SignificantLen`, предикат на 26 реальных строках |
| Арм без фактора | заложен в P1 (голая частота) и в замер §2.3 (арм без примера) |
| Декой | §3.4, 10% строк, посчитан деньгами |
| Право сказать «не надо» | использовано трижды: судья (не строить), `allow_short` (не «просто строка»), пар-файл kwic для zh-ru (не шипуем) |
| Деньги названы до стройки | §8, ≈$0.009 + $0 |
| Оси каждого пункта | §8, столбцы «двигает ли снапшот» |
| Общность | по каждому пункту отвечен вопрос «пара, которой нет в репо, без правки Go» |
| **НЕ покрыто** | размер популяции банкноты на полной книге (нужен холодный старт) · вторая пара · вторая модель · разметка «терм/мусор» одного разметчика · доступность сохранённого сырья пакета-8 для приёмки §2.5 п.5 |
---
## §10 Что на владельце
1. **Санкция на стройку пунктов 17 §8** (языковой экран + счётчик + фильтр свёртки + кавычки +
агрегация + `kwic_width` в пар-данные + doc-хвост). Всё детерминированное, $0, прод не дорожает.
2. **Санкция на два замера полигона:** пример формата в промпте ≈**$0.009** и `consistency` **$0**.
3. **Решение по §3.4:** ставить ли семантический экран вообще, и если да — J1 перед терминологом,
≈$0.025/книга. Моя рекомендация — отложить до P1 и холодного старта.
4. **Решение по §6:** идёт ли мини-прогон D39.40 в ХОЛОДНОЙ конфигурации (пустой банк). Одни деньги —
пять ответов, включая единственную неизмеренную цифру пакета-8.
5. **Решение по §5:** добавлять ли `allow_short` для `蛊`/`转` — и подтверждение, что это делается на
холодном прогоне, где перепокупка книги стоит ноль.
6. **Вопрос к полигону (не к владельцу):** живо ли сохранённое сырьё 147 вызовов пакета-8 — на нём
языковой предикат принимается на настоящих данных вместо фикстур, за $0.
**СТОП.** Стройка — после ратификации. Ни строки продового кода эта сессия не изменила и не коммитила.

View file

@ -225,7 +225,10 @@ recall против подписанного сида объявляется Н
уровне множеств выглядит как разъезд в 97%. И объём даёт банкнота: 67 против 13, то есть **пять
шестых кандидатов в карту подписи приходит не от майнера**.
По типам строк банкноты: `term` 67 строк · `title` 13 · `name` 4 · `place` 3. То есть банкнота
По типам строк банкноты в ТОМ ЖЕ основном прогоне (71 строка): `term` 57 · `title` 9 · `name` 3 ·
`place` 2. *(Первая редакция давала здесь 67/13/4/3 — счёт по ТРЁМ прогонам рядом с числом
поверхностей одного; смена популяции без оговорки, поймано ревью оркестратора D39.50, исправлено.)*
То есть банкнота
эмитирует ровно тот класс, который майнер не эмитирует НИКОГДА (фильтр типа `name|place|title`).
Среди её предложений — `光阴之河` (Река времени), `十大奇蛊`, `九转境界`, `力量蛊`, `智慧蛊`,
`青铜真元`, `紫府`, `秘法`, `遗藏`, `蛊室`, `性命交修`, `开窍大典`.