textmachine/docs/architecture/11-implementation-plan.md

140 KiB
Raw Permalink Blame History

План реализации пере-прогонного стека (слои 1+4+5 + данные слоя 2) — дизайн-синтез, 2026-07-19

Статус: РАТИФИЦИРОВАН — D39.12 (19.07, оркестратор №7); дизайн-оф-рекорд бэкенд-стройки. Проектный документ дизайн-синтез-сессии (D39.11): синтез всей исследовательской программы арх-ресета (research/1920, exp15/16, аудит 08, дефект-классы слепого чтения) в план стройки с верифицированными ($0) алгоритмами. Ревью-провенанс (три рубежа): ревью-2 оркестратора (F1F11, все внесены — 36 маркеров) → пост-фикс 7-агентный адверсариал-воркфлоу сессии (5 находок впитаны, вкл. переписанный F8 и mined-write-путь) → 3-агентный подтверждающий пас (2 резидуала закрыты). Бэкенд строит СТРОГО по этому плану (BACKEND_PLAN11_SESSION_PROMPT.md, единый пак — решение владельца); 4 гейтнутых арма — §11.

Источник истины: при конфликте побеждает D-лог (05-decisions-log.md, D1D39.11). Все file:line — против HEAD 766a60e. Все пороги/коэффициенты — число + источник. $0-верификации — eval/design11/, результаты в books/gu-zhenren/design11/ (вне git); сводка — VERIFICATION_RESULTS.md.

Дисциплина цитат: аудит 08 и D-лог местами несут устаревшие memory.go-строки (~+11…+26 к HEAD: renderEditorConstraintBlock :515 не :489, suppressContained :684, dispositionFor :441; санитайзер = v6 не v4). Этот план цитирует HEAD-строки.


§0. Рамка: доказанная база и зоны

Строим (ратифицировано D39.x; в план внесено, НЕ пере-открывается): волновой раннер W0→W1(∥)→W1.5→W2(∥); бюджет чанкера в ВЫХОДНЫХ токенах (фертильность 1.20/0.39); банк W1.5 слоя 4 (детектор V-C WHICH + банкнота dst + Палладий + консолидация §C2 + алиас-ярус-1 + карта подписи); декаплинг draft=мелкий-чанк / edit=крупная-единица; свап редактора glm→mistral/deepseek-pro; дефект-класс чекеры + данные пакета пары.

НЕ строим (доказано/ратифицировано): сцен-детекция и carryover-машинерия (D39.7/8, метрики под полом 0.126 И читатель); DP/logical-граница чанкера (Q1: «граница не рычаг»; Q1a 0/7); ко-оккуренция для dst (canon-recovery 0.200.68 < 70%); LLM-сегментация/ML-сцены/эмбеддинг-границы/RAG-оверлап; фронтир в дефолт; жанр-паки; локальный 9B-споттер (код ≥ локаль). Парковки (не тестировано, консервативный дефолт — НЕ строим, НЕ хоронить): lookahead (Q3b), diff-редактор §C2 (Q4b), reflow-отдельным-пассом (Q4c), широкое-окно/узкая-эмиссия (Q2c), W2.5-швейный пасс (Q5). Каждая парковка помечена «не тестировано» — диспозиции в §9 (lookahead/diff-редактор через §1(е)/E.7; Q4c/Q2c/Q5 — явные строки в §9).

Столпы (инварианты 18 backend/README.md, ломать нельзя): деньги/durability (committed==SUM, kill-9); снапшот-дисциплина (правка wire/вердиктов = громкий resnapshot; все правки этого плана → ЕДИНЫЙ resnapshot §8); детерминированный банк (не вектора); эхо-мина DeepSeek (thinking НИКОГДА не off); 18+; конфиг-первое ядро; экспорт-контракт (внешняя экстракция ТОЛЬКО tmctl export); golden-гард детерминизма; инвариант общности §0.1 (любая книга/пара/структура; пара = ось данных).

Сводка $0-верификаций (все PASS, детали в каждом WS (г)):

WS Верификация Результат
WS2 фертильность независимо пере-выведена 1.1978/0.3852, R²=0.9633 (Δ<3e-5); классиф.-mismatch на zh = 0
WS2 нарезка на 25 главах target_out=1797 → тот же СЧЁТ 56 чанков (4 ре-сегм / 2 абзац-сдвига, НЕ byte-identical); edit-единица: 8000-арм 25/25 в 2k8k, ратиф. дефолт 3200 → 37 единиц (11 глав делятся; размер ГЕЙТИТСЯ Q2a)
WS3 майнер-v1 golden катастроф {方源:0,蛊:1,蛊师:2,古月:21} EXACT (вариант A / Python-референс; при ратиф. дефолте B古月:22, скрин PASS — §3(г)/(д) split-фикстура); 13618 канд.; recall 0.965/0.932
WS3 сид-дельта + линт 61 терм, 0 фейл-лаудов; флагает D38-дефект как draft ⟨проверить⟩
WS4 банкнота пере-парс 95 строк/65 src/0 fail/0 trunc EXACT; hash resume-стабилен
WS5 чекеры 6/6 позитивов; DC1/2/6 n=1 (реальные, FP-rate §5д); DC3 3.5% (1 паратекст + 1 narrative-coref на hidden); DC5 74%/42% → не гейт; DC7 не покрыт $0
WS1 деньги под ∥ + wall-clock money-инвариант ALL PASS (границы ПАРАЛЛЕЛЬНЫ); within-wave compute ≈7.3× (end-to-end + W1.5-стоп)
WS6 COGS per-арм glm-5 $0.335 / mistral $0.205 / deepseek-pro $0.117 (25-гл, warm/valley)

§1. WS1 — Волновой раннер (слой 1 / оркестровка)

Целевой раннер: W0 (чанкер + контекст-пакеты, $0) → W1 черновики параллельно → W1.5 (стоп-граница: банк-синк, подпись владельца, resnapshot) → W2 редактуры параллельно. Сегодня перевод строго последователен: bookrun.go итерирует плоский список чанков, translateChunk (chunkrun.go:90) гонит ВСЕ стадии одного чанка по порядку под ОДНИМ snapID. Волны разносят: W1 = только draft-стадия по всем чанкам; W2 = только edit-стадия по всем единицам; W1.5 между ними.

(а) Что меняем (модули, file:line)

  • runner.go — новый волновой драйвер (или bookrun.go расширяется режимом waves); закрыть гонку lazy-init Runner.client (runner.go:171-181): r.clients map[string]llm.LLMClient читается+пишется без лока.
  • bookrun.go — цикл книги переходит на волны; per-chapter sticky-reset (stickyWin=nil, HEAD ~bookrun.go:144) переезжает в W0-precompute.
  • chunkrun.gotranslateChunk расщепляется на per-волна проходы; sticky-цепочка (chunkrun.go:107-115: memSel=r.memory.Select(...)activeIDs → следующий stickyPrev) precompute'ится в W0.
  • snapshot.gosnapshotID() (:101-279) становится пер-волновым (см. (в)).
  • stagerun.go — деньги/чекпоинт-путь (ReserveSettleWithCheckpoint, :312-449) без изменений семантики, но вызывается из N горутин; client(model) предвычислен.
  • config/pipeline.go — новый конфиг waves.workers (число воркеров волны — НЕ fanout.workers: Fanout занят C2-кандидатами, CheckRunnable отвергает candidates>1 pipeline.go:208-209; оси ортогональны) + pricing_windows провайдера + пер-модельный rate-guard (ревью-2 F4): models.yaml max_concurrency/опц. min_interval НА МОДЕЛЬ (конфиг, НЕ хардкод); rate-guard = транспорт-ось, НЕ снапшот (wire-байты не трогает).

(б) Алгоритм (детерминизм явно)

W0 (детерминированный, $0):

  1. SplitChunks (draft-чанки, WS2) + сборка edit-единиц (WS2).
  2. Predвычислить sticky-цепочку: один упорядоченный проход по чанкам КАЖДОЙ главы; для чанка i stickyPrev_i = union предыдущих stickyDepth=2 чанков той же главы (порт bookrun.go unionSticky), затем memSel_i = memory.Select(chunk_i, chapter, stickyPrev_i, budget), activeIDs_i кормит stickyPrev_{i+1}. Select — ПУРЕ детерминированная функция (chunk, chapter, stickyPrev, budget, frozen-bank) (memory.go:324), src-производна, БЕЗ модель-выхода → precompute воспроизводит БАЙТ-в-БАЙТ ту же инъекцию, что онлайн-цепочка последовательного раннера. Инвариант: precompute'нутые инъекц-байты ≡ байтам текущего раннера (golden-тест в (д)) → sticky-переезд НЕ двигает request_hash, НЕ строка манифеста resnapshot. ⚠ Byte-identity держит только над BASE-банком и над НОВОЙ нарезкой (WS2 сдвигает 4 чанка); golden-тест (д) сверяет против нового чанк-сета, не старого. (Параллелизм байты НЕ меняет — Select src-производна, порядок фиксирован; ревью-1 CONFIRMED.)
  3. Eager-build клиентов: множество моделей известно из Pipeline.Stages[].Model EscalateTo до старта → построить ВСЕ llm.LLMClient в W0 (BuildClient, clients.go:15), заполнить r.clients ДО любой горутины. Закрывает гонку runner.go:171-181 без лока (карта только читается в волнах; r.client() вызывается ровно в одном месте stagerun.go:343 с model∈{st.Model, EscalateTo} — перечисление ПОЛНОЕ). Защита от будущей 3-й модель-оси (channel B / annotator, stagerun.go:476-478): в волновом режиме client() делается READ-ONLY (panic/error на miss), чтобы не-перечисленная модель не пере-открыла гонку тихо. Прочие разделяемые хендлы: templates (построена раз в loadTemplates, read-only в волнах — safe), memory *MemoryBank (материализован раз, immutable — safe), Store (single-writer сериализует записи — safe), Log (slog thread-safe).

W1 (∥): N воркеров тянут draft-чанки из очереди; каждый — runStage(draft-стадия) под snapshot_W1. Деньги: ReserveCompleteSettleWithCheckpoint (stagerun.go) — сериализуются через single-writer store-пул (store.go:71 MaxOpenConns=1, BEGIN IMMEDIATE). Ретрай чанка i изолирован: его вход — чистая функция (source_i, frozen-bank, snapshot_W1), независим от чанка j (свойство волн — нет разделяемого мутабельного входа).

W1.5 (стоп-граница, $0 к провайдерам кроме опц. облачной Z1-адъюдикации): банк-майнинг из W1-черновиков (WS3) → алиас-кластеризация ярус-1 → канон-предложения (draft) → карта подписи (сайдкар) → подпись владельца (approve/правка/draft/расклеить) → эмиссия сид-дельты → reseed → snapshot_W2. Механика стопа (см. (в)).

W2 (∥): N воркеров тянут edit-единицы (главы, WS2); каждый — runStage(edit-стадия) под snapshot_W2, читая draft из W1-чекпоинта (prev = draft-текст единицы). W1-чекпоинты под snapshot_W1 НИКОГДА не пере-гоняются → W1 не пере-оплачивается. Инъекция редактора W2 (ревью-2 F3, специфицировано): edit-стадия единицы получает СВЕЖИЙ memory.Select по тексту ВСЕЙ единицы (глава) над ОБОГАЩЁННЫМ банком (sticky-окно на уровне глав вырождено: единица = глава); констрейнт-блок renderEditorConstraintBlock = CONFIRMED-only, формат src→dst (решение WS2 §2(б)). Явный запрет: НЕ реюзить per-чанк memSel из W0 (chunkrun.go:107-114) — тот посчитан над BASE-банком, W2 поехала бы БЕЗ майненого банк-энфорса. Это НЕ противоречит инварианту секвенирования (в): свежий Select кормит ТОЛЬКО edit-стадию (CONFIRMED-only enriched-банк), draft-стадия по-прежнему НЕ пере-рендерится/не пере-хешируется. Пин — (д): инъекция W2 содержит подписанные на W1.5 mined-термы.

Пер-модельный rate-guard (ревью-2 F4, НЕСУЩЕЕ для ∥): пер-ВЫЗОВНЫЙ 429/Retry-After УЖЕ есть в адаптере (httpllm.go:27-29,138-140, читается+уважается, кап maxRetryAfterWait=5мин :76; тест TestRetryOn429HonoursRetryAfter httpllm_test.go:168) — до WS1 раннер БЫЛ последователен, проблема N-параллельности НЕ возникала. Гэп именно N-конкуренция. Волновой диспетчер добавляет пер-модельный семафор конкуренции (+опц. пейсинг min_interval) из models.yaml + телеметрию 429-плотности. Rate-guard НЕ снапшот-ось (транспорт, wire-байты не трогает). ⚠ Известный лимитер: mistral-large-latest ~48% retry-fails @1.3s интервала, max lat 64.7s (судейский eval-риг exp15 §7.6 REV.2; grok 0%); расширение 1.3→2.6s эффекта НЕ дало (49.5→48.2%) → похоже на токен-бакет/конкуренцию, тир-зависимо → mistral-арм НЕ идёт через прод-путь до rate-guard (WS6 пред-условие). Заготовка-блок для 00-provider-quirks.md — §12 (вносит оркестратор при лендинге, зона полигона).

Money под параллелизмом (доказано верификацией): потолок проверяется ВНУТРИ Reserve immediate-транзакции (ledger.go:56-72: committed+reserved+estimate ≤ ceiling) → конкурентные резервы N воркеров сериализуются single-writer'ом, overshoot ≤ одной макс-резервации. Урок D12 (гонка Runner.clients + commit-terms-on-boundary): клиенты предвычислены (закрыто); commit-терминов на границе джобы в этой архитектуре не применяется (банк консолидируется в W1.5, не на границе чанка).

(в) Конфиг + снапшот-механика — ПЕР-ВОЛНОВОЙ снапшот (несущее)

Сегодня snapshotID() — book-global, фолдится в КАЖДЫЙ request_hash (stagerun.go:268-269: snapID — прямой вход). Правка snapID = all-or-nothing re-bill (snapshot.go:26-34); content-addressed reuse (msgsContentHash) гейтится НЕ-изменённым снапшотом → через resnapshot не срабатывает (D15.2 — не построен). Проблема: банк входит в snapshotID через MemoryVersion (snapshot.go:191,229); наивный resnapshot после W1.5 сдвинул бы snapID для ВСЕХ стадий, включая W1 → пере-оплата W1 → «две переоплаты».

⚠ Коррекция после адверсариал-ревью-1 (несущее): «переоплата ОДНА» держит НЕ снапшот, а ВОЛНОВОЙ ИНВАРИАНТ СЕКВЕНИРОВАНИЯ. request_hash фолдит рендер msgs НАПРЯМУЮ (stagerun.go:268-269), не только через snapID; resume-fast-path отдельно сверяет cs.ContentHash == msgsContentHash(msgs) (stagerun.go:65,79). W1-инъекция переводчика (renderGlossaryBlock, memory.go:480,484-487) эмитит ВСЕ строки с непустым dst, ВКЛЮЧАЯ AMBIGUOUS (auto/draft, помечены ⟨проверить⟩) — а WS3-майнинг добавляет до 21 draft-строки с dst. ⇒ если W2-проход когда-либо ПЕРЕ-рендерит/пере-хеширует draft-стадию над обогащённым банком, её msgs→contentHash→request_hash меняются → miss → re-bill W1, даже при byte-identical snapshot_W1. Несущий инвариант: W2-проход НИКОГДА не трогает рендер/хеш draft-стадии; редактор получает черновик из W1-ЧЕКПОИНТА (final_hash→response_text), не из свежего draft-рендера над обогащённым банком. Это ПРЯМО противоречит текущему translateChunk, который считает memSel ОДИН раз и рендерит инъекции ОБЕИХ стадий из него (chunkrun.go:107-114) — наивный волновой сплит, пере-считывающий memSel в W2 для content-verification, пере-выведет draft-инъекцию над обогащённым банком и споткнётся ровно тут. Волновой раннер обязан разнести: W2-проход рендерит ТОЛЬКО edit-стадию, черновик читает по final_hash.

Роль пер-волнового снапшота (уточнено): он НЕ предотвращает re-bill (это делает инвариант выше) — он избегает громкого resnapshot-СТОПА (stagerun.go:45-49): на резюме-после-W1.5, если владелец промоутнул майненые строки в approved (это ДВИГАЕТ MemoryVersion, memory.go:264-282), book-global снапшот сделал бы snapshot_id уже-готовых draft-джоб ≠ пере-вычисленному глобальному → hard-стоп. Пер-волновой снапшот держит W1-джобы на snapshot_W1 (стабилен) → стопа нет. snapshotID вычисляется пер-волна, фолдя ТОЛЬКО стадии этой волны + состояние банка на момент волны. Хранение уже пер-стадийное — jobs UNIQUE(book_id, chapter, stage) (migrate.go:37), каждая джоба несёт свой snapshot_id. Под-специфицировано (реальная ре-архитектура, не кноб — бэкенду построить): сегодня snapshotID() фолдит ВСЕ стадии + ОДИН book-global MemoryVersion в ОДИН хеш (snapshot.go:229), TranslateBook апсертит РОВНО один снапшот (bookrun.go:117-123). План требует: раннер вычисляет ДВА MemoryVersion (base vs enriched, разбивка по Source ниже), апсертит ДВА снапшота (W1/W2), пинит стадии волны к своему. Сейчас все стадии получают ОДИН snapID; план делает MemoryVersion (и stage-plan) компонентом волны:

  • snapshot_W1 = f(brief, chunkerVersion, draft-stage-plan, base-bank-version, coverage/sanitizer/classifier/…).
  • snapshot_W2 = f(brief, chunkerVersion, edit-stage-plan, enriched-bank-version, …). W1.5-обогащение банка двигает ТОЛЬКО enriched-bank-version → ТОЛЬКО snapshot_W2. W1 стабилен.

Механизм стабильности W1 на РЕЗЮМЕ (несущее, закрывает re-bill-дыру): после W1.5 сид ОБОГАЩЁН (дельта влита) → наивный пересчёт snapshot_W1 из ТЕКУЩЕГО (обогащённого) банка дал бы enriched-memory-version → mismatch с замороженным в W1-джобах snapshot_id → resnapshot-стоп → re-bill W1. Фикс использует поле Source (glossary.go:36, seed|ruby|auto + новое mined WS3): base-bank-version = хеш approved-строк с Source∈{seed,ruby} (БЕЗ mined); enriched-bank-version = хеш ВСЕХ approved-строк (вкл. Source:mined). Майненые строки исключены из W1-фолда → computeMemoryVersion-вариант для W1 пере-вычисляется из НЕ-майненых строк, стабилен через W1.5-обогащение (майнинг только ДОБАВЛЯЕТ mined-строки, не трогает seed/ruby) → snapshot_W1 воспроизводится идентично на резюме → W1-чекпоинты ХИТ, не пере-оплачиваются. Детерминировано (фильтр по Source + сортировка GlossaryForBook). Пиновый тест — (д).

⚠ Скоуп подписи W1.5 = ТОЛЬКО Source:mined (ревью-2 F2, инвариант — прямое следствие фикса выше): mid-run подпись владельца на W1.5-границе трогает ИСКЛЮЧИТЕЛЬНО Source:mined-строки (промоут draft→approved, правка dst майненого терма). Правка/промоут APPROVED Source∈{seed,ruby}-строки ДВИГАЕТ base-bank-version → mismatch с замороженным в W1-джобах snapshot_W1 → resnapshot-СТОП / re-bill W1 — поэтому разрешена ТОЛЬКО как ОТДЕЛЬНОЕ resnapshot-событие ВНЕ прогона (пред-прогонный reseed §8), НЕ на W1.5-границе. (Уточнение precision: DRAFT-status seed/ruby-строка НЕ фолдится в base-bank-version [gate off, memory.go:264-266], но W1-инъектится через renderGlossaryBlock → её правка = ТИХИЙ per-чанк re-bill через contentHash-путь stagerun.go:72-79, не громкий стоп; реалистично seed/ruby = approved, пин (д) это разделяет.)

  • Write-path Source:mined (ревью-2 пост-верифай, НЕСУЩЕЕ — закрывает грунт-гэп критика): loadGlossarySeed БЕЗУСЛОВНО штампует Source:"seed" (memseed.go:131) — наивный reseed W1.5-дельты через него проштамповал бы майненые термы Source:seed → они попали бы в base-bank-version → сдвинули snapshot_W1 → сломали «переоплата ОДНА». ⇒ персист майненой дельты после подписи идёт ОТДЕЛЬНЫМ mined-write-путём (штамп Source:mined, зеркало rubyToCandidates's Source:"ruby" memseed.go:352) — НЕ через немодифицированный loadGlossarySeed (хардкодит Source:"seed" :131) и НЕ расширением seedTerm-схемы (та остаётся БЕЗ новых полей — §3(б); Source-класс живёт в сайдкар-карте подписи). Пин — WS3 (д).
  • Промоут 甲乙丙丁/四代族长 (DC-7): майнер СЮРФЕЙСИТ их как Source:mined draft (флаг-для-видимости §3(б)); но их промоут в approved-канон = ЭЛЕВАЦИЯ в Source:seed (канон грейд/титул = base-seed-класс, не run-time mined) → поэтому строго в пред-прогонный reseed (§8, MemoryVersion двигается), НЕ в W1.5-подпись. Пин-тест — (д): правка approved seed-строки после W1 → ГРОМКИЙ resnapshot-стоп, не тихий re-bill.

Механика W1.5 для ОБОИХ состояний гейта (доказуемо «переоплата ОДНА»):

  • PostcheckGate=false (текущий дефолт): computeMemoryVersion(rows, gateOn=false) фолдит ТОЛЬКО approved-строки (memory.go:264-266). Майненые auto/draft НЕ двигают снапшот; их вливание меняет лишь инъекцию стадий, которые их инъектят. На момент W1 банк = base (майнинг идёт ПОСЛЕ W1) → W1-инъекция = base. В W1.5 подпись владельца промоутит часть в approved → enriched-bank-version меняется → snapshot_W2 двигается ОДИН раз. Auto/draft (AMBIGUOUS) в редакторский блок (renderEditorConstraintBlock, CONFIRMED-only, memory.go:515) НЕ входят.
  • PostcheckGate=true (hard-gate, висит за пере-замером D24.4/D28.1): computeMemoryVersion(rows, gateOn=true) фолдит ВСЕ строки вкл. auto/draft + gate:-флаг (memory.go:251-266, snapshot.go:86-93). Но на момент W1 майненых строк ещё НЕТbase-bank-version реально base. В W2 банк обогащён → enriched-bank-version включает все майненые строки. Пер-волновой снапшот держит «одну переоплату» и здесь: W1 под base, W2 под enriched.

UX-решение (стоп посреди книги) — владельцу (§10 вопрос W1.5-UX; §E-5 research/20). Дефолт плана: W1.5 — явная стоп-граница (не mid-run-append): раннер завершает W1, пишет карту подписи, ОСТАНАВЛИВАЕТСЯ; владелец подписывает; tmctl возобновляет с W2 под snapshot_W2 (--resnapshot принимает сдвиг). Sticky/scene-инерция precompute'ится в W0 (src-производна) — не ломается параллелизмом.

Scheduler-aware диспетчер: ценовые окна = конфиг провайдера (providers.json/models.yaml), не хардкод. Вендор-чеки ($0, разрешены):

  • (а) DeepSeek пик/офф-пик — РАЗРЕШЁН (флаг D39.10-6): два механизма из разных эр, НЕ конфликт. Легаси off-peak СКИДКА 16:3000:30 UTC 50%/75% — только V3/R1, НЕ переносить на v4. Актуально для v4: PEAK-надбавка ×2 в UTC 01:0004:00 06:0010:00 (пики Пекин 0912 & 1418); офф-пик = baseline (доп. скидки НЕТ). ⚠ ×2 подтверждён ТОЛЬКО вторичкой (TechNode/explainx/KuCoin); офиц. api-docs.deepseek.com/quick_start/pricing на 2026-07-19 — ФЛЭТ, без peak/off-peak. Правило двух направлений: держать ×2 как РИСК-ПИН, ре-чек у катовера 24.07. Раннер: платные DeepSeek-джобы вне пик-окон; нельзя — закладывать ×2, не блокировать. (URL: docs §157167.)
  • (б) ZAI/GLM префикс-кэш — РАЗРЕШЁН (§E.8-бис): implicit префикс-кэш подтверждён на glm-5 (docs.z.ai/guides/capabilities/cache); cache-hit = $0.20/1M (80% от input $1.00), НЕ 50% из иллюстрации доки; хранение бесплатно (limited-time); TTL официально НЕ документирован (агрегаторные «5мин/512ток» НЕ подтверждены). Warm-then-fan оправдан: warming-вызов платит полный input, фан-аут-соседи — cache-hit. Инвариант байт-идентичного ПРЕФИКСА ([система+мандаты]+[глоссарий сорт-ключи]→[волатильный чанк в хвост], research/19:506): любой timestamp/ счётчик-ретрая/«почему упало» — только в ХВОСТ (:213). Бэкенд УЖЕ читает cached_tokens (httpllm.go:232-255); реальную долю cache-hit верифицировать в ledger (эмпирика бьёт веру, TTL-риск).
  • (в) Слаги DeepSeek 24.07 — РАЗРЕШЁН: deepseek-chat/deepseek-reasoner депрекация 2026-07-24 15:59 UTCоба на v4-flash (chat=non-thinking, reasoner=thinking). Наш прод deepseek-v4-flash/deepseek-v4-proНЕ задет. Действие бэкенду: grep configs/models.yaml + eval-скриптов на legacy-слаги + live GET /models. (research/19 §E.8: eval-скрипты несут legacy-слаги — exp15/preflight_models.py:32, local_bench.py, editor_bench.py:36 — это eval-долг, не прод.)

(г) Верификация ДО стройки ($0) — ws1_wave_cogs.py, РЕЗУЛЬТАТ

  • Money-инвариант (mock, моделирует single-writer admission-ось ledger.go; НЕ полное зеркало — см. каветы): ALL PASScommitted==SUM($0.448), reserved=0, kill-9 без двойной оплаты (идемпотентный settle ledger.go:143 ON CONFLICT DO NOTHING). ⚠ Границы — ПАРАЛЛЕЛЬНЫЕ, не последовательные (адверсариал-ревью-1): (1) kill-9 теряет ≤N in-flight вызовов (recoverReservations обнуляет ВСЕ reserved_usd, store.go:219), каждый пере-оплачен ОДИН раз — не «≤1»; (2) overshoot: реальный Reserve ОТКЛОНЯЕТ при bookTotal+estimate>ceiling (ledger.go:67-71) → reserved потолок не пробивает; единственный overshoot-источник = settle бронирует cost при cost>estimate (pricier modelActual PriceForResponse stagerun.go:420; reasoning-overflow) → под N резервами bound = Σ(cost_iest_i). Mock это НЕ проверяет (cost=0.008<est=0.01 захардкожен) → закрепляет Go-тест в (д). Каветы неверности mock: проверяет ОДИН потолок (реальный book И day ledger.go:67-71); settle НЕ идемпотентен (реальный да, ledger.go:151-160) — обе оси в Go-тест (д). Реальную durability пинит Go+SQLite -race+kill-9 с N живыми резервами в (д).
  • Wall-clock (mock-латентности, LPT bin-pack): ВНУТРИ-ВОЛНОВАЯ compute-параллелизация sequential-compute 1896s → волны 8-воркеров 258s ≈ 7.3× (W1 121s + W2 138s). ⚠ End-to-end доминирован W1.5 человек-стопом (подпись владельца между волнами — неограниченное ожидание, в 7.3× НЕ входит); mock — единый LAT_PER_KTOK для thinking-ON черновика и редактора (разные профили). ⚠ 7.3× НЕ учитывает rate-limit-столлы (ревью-2 F4): пер-модельный семафор + 429-бэкофф сериализуют вызовы к лимитированной модели (mistral ~48% retry-fails) → реальный wall-clock волны ниже compute-идеала. Порядок-величины; точные латентности — прод-телеметрия.
  • Структура: 25 глав → 56 draft-чанков (target_out 1797) + 25 edit-единиц. COGS — WS6 (г).

(д) Тест-спека ПОСЛЕ стройки

  • go test -race ./... над волновым раннером: N-воркер прогон, детектор гонок чист (клиенты/store/templates/memory).
  • kill-9-тест с N живыми резервами: убить процесс между Reserve и SettleWithCheckpoint для N чанков → рестарт: recoverReservations обнуляет резервы, ретрай оплачивает ≤N вызовов ОДИН раз каждый; committed==SUM(checkpoints.cost) (порт store/kill9_test.go на волны). + overshoot-тест cost>estimate (pricier modelActual): overshoot ≤ Σ shortfall.
  • Golden: инъекц-байты W0-precompute sticky ≡ байтам текущего последовательного раннера над НОВЫМ чанк-сетом WS2 (fresh↔resume; sticky не двигает request_hash над base-банком).
  • НЕСУЩИЙ пин (ревью-1 F1): draft request_hash/contentHash БАЙТ-СТАБИЛЕН под ОБОГАЩЁННЫМ банком — W2-проход не пере-рендерит draft-стадию; редактор читает черновик по final_hash. (Не просто «snapshot_W2 двигается».)
  • Пер-волновой снапшот: промоут майненых→approved двигает ТОЛЬКО snapshot_W2 (W1-джобы не ловят resnapshot-стоп); base-bank-version=хеш Source∈{seed,ruby}-строк стабилен через W1.5.
  • Пин скоупа подписи W1.5 (ревью-2 F2): правка/промоут Source∈{seed,ruby}-строки ПОСЛЕ W1 → ГРОМКИЙ resnapshot-стоп (не тихий re-bill W1); правка ТОЛЬКО Source:mined-строки → двигает лишь snapshot_W2, W1 стабилен.
  • Race-тест Runner.clients (D12-урок): client() read-only в волнах, panic на miss не-перечисленной модели.

(е) Открытое / маршрутизация

  • Соседи-черновики как контекст W2 — НЕ ратифицированы (Q3a null + гипотеза CJK-протечки V2-брака §7.9: src- carryover → больше исходника → выше протечка). Дефолт: без них. Если бэкенд захочет — обоснуй + дешёвый гейт.
  • Lookahead (Q3b) — парковка (не тестировано). Не в дефолт.
  • W1.5-UX (стоп посреди книги vs автопродолжение) — вопрос владельцу (§10).
  • Пик-надбавка DeepSeek ×2 — риск-пин, ре-чек 24.07 (§10).

§2. WS2 — Чанкер-бюджет + крупная edit-единица (слой 1)

(а) Что меняем (file:line)

  • chunker.go — бюджет из const targetChunkTokens = 1500 (:47, единица estTokensFrom=cjk+other/3 :173-181) → конфиг per-pair в ВЫХОДНЫХ токенах через фертильность; Chunk{Chapter,ChunkIdx,Text} (:32-36) расширяется аддитивно (EstOut float64, OversizedSentence bool, EditUnitID int). Мега-ПРЕДЛОЖЕНИЕ (packSentences:129-152: своё оверсайз-под-чанком БЕЗ клауза-сплита и БЕЗ флага) — решить (см. (б)).
  • config/pipeline.go — снести мёртвые STMDepth/OverlapTokens (:47-48, carryover не строим); добавить Segmentation{DraftBudgetOut int, EditCeilingOut int, Fertility{CJK,Other float64}} (per-pair, снапшот-folded).
  • coverage.go — инвариант floor: draft-бюджет держит чанки над Gates.Coverage.MinChunkChars (pipeline.go:159, дефолт 500); связь-инвариант, не жёсткий (хвосты глав легально < floor — документ. граница chunker.go:42-45).
  • snapshot.gocontextSnap (:15-21) теряет STMDepth/OverlapTokens; добавляется segmentationSnap (бюджет+фертильность+edit-ceiling, фолд как coverageSnap).
  • новый slojpjat-омиссия-бэкстоп в слое 5 (см. WS5/(б)) — компенсатор зоны деградации.

(б) Алгоритм

  • Draft-чанк = мелкая единица (COGS/coverage/выравнивание): greedy-упаковка абзацев (как сейчас, appendChapterChunks), но бюджет = est_out(chunk) = 1.1978·cjk + 0.3852·other (ВЫХОДНЫЕ токены) вместо input-est. Оверсайз-абзац → packSentences; НИКОГДА не резать предложение. (WS2 (г): на zh смена единицы near-нейтральна — тот же СЧЁТ 56 чанков при target_out=1797, НО 4 из 56 чанков ре-сегментируются (2 реальных абзац-сдвига на границе накопления, где est_out other-вес 0.3852 vs input-est integer other/3 расходятся) → НЕ byte-identical → законный chunkerVersion bump §8. Ни один target_out не даёт 0 diff.) strategy-ось (greedy|logical) — заложить как СЕАМ (аддитивное поле), но наполнение logical НЕ строить (Q1 «граница не рычаг», Q1a 0/7). DP §B1.5 (штрафы B0:0/B1:1/B2:3/B3:6/B4:10, веса w_size=3/w_bound=60/w_dialog=200) — справочно в research/19, НЕ реализуем.
  • Мега-предложение (решение плана): packSentences пропускает мега-ПРЕДЛОЖЕНИЕ оверсайз-под-чанком без флага. Решение: passthrough + флаг OversizedSentence=true (не аварийный клауза-сплит ;:,、 из §B1.4). Обоснование: клауза-сплит = граничная машинерия (анти-скоуп); «деградация не молчит» удовлетворяется ФЛАГОМ (наблюдаемость). Мега-абзац уже покрыт (packSentences). Стихи/паратекст — как обычный текст (§0.1: не спец-кейсить структуру книги); стих-РЕНДЕР — пак-политика WS5-DC5.
  • Edit-единица = глава — ДЕКАПЛИНГ draft/edit ратифицирован (D39 п.4, exp14 претензия-1: редактору нужна КРУПНАЯ единица для меж-чанковой когезии). Глава = естественная дискурс-граница; глава > EditCeilingOut → деление по АБЗАЦНЫМ границам greedy до потолка (never-split-paragraph). На 25-гл книге при дефолте 3200: 11 глав делятся → 37 edit-единиц (при гейтнутом 8000-арме — 25 цельных глав, 0 > 8000, max 6528, все в 2k8k; ревью-2 F1 — детали (г)). ⚠ РАЗМЕР единицы ГЕЙТИТСЯ (ревью-1 F1, не «безопасно»): span-омиссия Q2a НЕ гонялась; omission-бэкстоп WS5 — сеть ENTITY-уровня (сущность есть где-угодно), СТРУКТУРНО СЛЕП к пропуску клауза/сцены внутри 6.5k-единицы, чьи сущности повторяются (сам reflow_omission.py:205-212: «NOT evidence big chunks omission-safe… needs SPAN-JUDGES; Q2b cannot ratify edit=chapter, only Q2a»). Решение: глава-единица — ЦЕЛЬ; для пере-прогона EditCeilingOutконсервативный дефолт ≤3200 out (край нижней зоны деградации, а не 6.5k), большая-глава-арм ГЕЙТИТСЯ платными Q2a span-судьями (прогон-протокол WS6/(д)) + чтением владельца (2-претензии ловят span-омиссию). Owner-развилка §10. Единица маппится на чекпоинты: деньги/resume per-СТАДИЯ-волна, НЕ per-микро-чанк — против инварианта №1: чекпоинт = единица оплаты стадии, волна = последовательность.
  • Сайзинг edit-единицы (ревью-2 F8, ИСПРАВЛЕНО пост-верифай): unit-awareness сайзинга УЖЕ автоматическая — stagerun.go:87-90 (D2.5) сайзит max_tokens от sizingText=prev (для стадии >0 = ЧЕРНОВИК всей единицы, НЕ источник): EstimateTokens(prev)·MaxOutputRatio + пер-модельный флор D24.3 (applyModelFloor); Reserve тоже text-proportional по msgs. ⚠ НЕ сайзить от est_out единицы (первичная формулировка F8 «от est_out» НЕВЕРНА): est_out=1.20·cjk+0.39·other ИСТОЧНИКО-производна и в glm-4.6 output-token-пространстве (≠ эвристика EstimateTokens cjk+other/3), а D2.5 прямо сайзит редактора от ЧЕРНОВИКА (~1.9× источника) — сайзинг от источника недо-бюджетит и ложно-триггерит length-ретрай. ⇒ единственное требование: edit-единица передаёт СВОЙ ПОЛНЫЙ черновик как prev (волновой дизайн W2 это и делает, §1(б)); НОВОЙ формулы сайзинга НЕТ → строки манифеста §8 F8 не требует. Инъекция единицы (ревью-2 F3, детали §1(б)): свежий memory.Select по тексту ВСЕЙ единицы над ОБОГАЩЁННЫМ банком (НЕ реюз per-чанк W0-memSel над base); формат src→dst, CONFIRMED-only.
  • Формат банк-констрейнтов редактора (открытый вопрос D30.1, memory.go:505-511 — решается ЗДЕСЬ, парковка запрещена): сегодня renderEditorConstraintBlock (:515) даёт ГОЛЫЕ dst-формы (- «dst», CONFIRMED-only, дедуп по dst). Решение: перейти на src→dst-мэппинг (- 源термин → «dst»), как у переводчика. Обоснование (единственное несущее — ревью-1 F4 снял два ложных): редактор БИЛИНГВ (D30.1, editor.md:1 видит источник) — привязка канона к src-терму устраняет неоднозначность при ОМОНИМИЧНЫХ dst (одна ru-поверхность на две сущности). ⚠ Отвергнутые обоснования: «AMBIGUOUS-строк станет больше» — НЕ-секвитур (блок CONFIRMED-only memory.go:519, майненые AMBIGUOUS сюда НЕ входят); «инвариант кэш-префикса» — нет (блок из per-chunk memSel.injected = волатильный ХВОСТ, не стабильный префикс). Мех-безопасность: RequestHash хеширует msgs вкл. блок → формат-правка меняет request_hash, тихого false-hit нет; CONFIRMED-only СОХРАНЯЕТСЯ; дедуп по (src,dst) лишь РАСШИРЯЕТ список (безвредно). Снапшот-фолд ЯВНЫЙ: формат рендера renderEditorConstraintBlock НЕ схватывается memoryMatchVersion (тот = нормализатор/матчер) — добавить версию формата рендера как folded-компонент снапшота (иначе loudness держится дисциплиной, не механизмом).

(в) Конфиг + снапшот

  • Фертильность {cjk:1.1978, other:0.3852} — конфиг слоя 2 (per-pair), снапшот-folded; пересчёт = громкий resnapshot (D30.9). Go грузит ТОЛЬКО коэффициенты (не токенайзер).
  • DraftBudgetOut дефолт 1797 (тот же СЧЁТ 56 чанков, что текущий input-est=1500 на zh; 4 чанка ре-сегментируются, 2 абзац-сдвига — НЕ byte-identical, WS2 (г)) — «tunable, ревизия по пере-прогону».
  • EditCeilingOut дефолт 3200 out (ревью-2 F1: приведено к WS2(б)/§7/§10-13/§11 — прежнее «8000» ЗДЕСЬ противоречило консервативному дефолту; 3200 = край нижней зоны деградации) — tunable. 8000 out = потолок ГЕЙТНУТОГО крупно-главного арма (верх зоны деградации research/19 §A.1.1), открывается ТОЛЬКО по платным Q2a span-судьям (§10-13), НЕ дефолт.
  • Снос STMDepth/OverlapTokens меняет contextSnap-структуру → строка манифеста resnapshot §8 (снапшот двигается).
  • Классификатор CJK (WS2 (г) находка §1.6): fertility_calib.is_cjk = ord-диапазоны БЕЗ Hangul; EstimateTokens (render.go) = \p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}. На ЭТОМ zh-корпусе — 0 сдвиг коэффициентов (verified — но КОРПУС-СПЕЦИФИЧНО: корпус не содержит спорных символов 々//CJK-радикалов, kana-марок ・ー). ⚠ ws2_fertility_verify классификатор B — НЕ верный Go-прокси (ревью-1 F3: исключает 々//радикалы, что unicode.Han включает; включает ・ー, что unicode.Katakana исключает) → скрипт НЕ доказывает ja/ko-общность. Решение: Go-порт использует РЕАЛЬНЫЕ unicode.RangeTable EstimateTokens (для общности §0.1); инвариантность задокументирована для ДАННОГО zh-корпуса, не как свойство классификатора; ja/ko-паритет — верифицировать Go-таблицами (после стройки). Коэффициенты не менять (impact на текущий zh = 0 подтверждён).

(г) Верификация ДО стройки ($0) — ws2_fertility_verify.py + ws2_chunk_sim.py, РЕЗУЛЬТАТ

  • Независимый пере-вывод фертильности (НЕ доверяя fertility_calib.json): f_cjk=1.1978, f_other=0.3852, R²=0.9633, MAPE 4.25%, n=52 — Δ vs артефакт < 3e-5. Классиф.-mismatch impact на zh = 0.0%.
  • Симуляция нарезки 25 глав: гистограммы est_out; target_out=179756 чанков (mean 1408.8, max 1795) — тот же СЧЁТ, что текущий input-est=1500, но 4 из 56 чанков ре-сегментируются (2 реальных абзац-сдвига; проверено прямым diff чанк-сетов: чанк 1 = 1777 vs 1778 симв, чанки 11↔12 и 54↔55 — абзац переезжает; ни один target_out не даёт 0 diff). Смена единицы near-нейтральна на zh, НЕ no-op/byte-identical. Floor: 4 чанка < 500 симв (хвосты). Edit-единица — ДВА конфига симулированы (ревью-2 F1, пере-сим at 3200, ws2_chunk_sim.py ceiling∈{8000,3200}): (i) ГЕЙТНУТЫЙ 8000-арм — 25/25 глав-единиц в зоне 2k8k out (0 глав > 8000, max 6528, hist: 14×[2k3.2k]/10×[3.2k5k]/ 1×[5k8k]); (ii) РАТИФИЦИРОВАННЫЙ дефолт 3200 — 25 глав → 37 edit-единиц (11 глав > 3200 делятся абзацно на 23 под-единицы; hist out: 26×[2k3.2k], 10×[01k] split-хвосты (< нижней зоны деградации — покрыты omission-бэкстопом), 1×[1k2k]). ⟶ «25/25 в 2k8k» описывал 8000-арм, НЕ дефолт; при 3200 единицы МЕНЬШЕ (безопаснее для span-омиссии — цель F1) ценой 10 мелких split-хвостов. omission-бэкстоп (слой 5) = СЕТЬ, НЕ доказательство безопасности (Q2a span-омиссия НЕ гонялась; бэкстоп entity-уровня слеп к span-пропуску → размер edit-единицы гейтится, см. (б)/(е)).
  • COGS-прогноз (no-cache upper bound): $0.403/25-гл; с кэшем — WS6.

(д) Тест-спека ПОСЛЕ стройки

  • Пин: est_out-бюджет на zh даёт СЧЁТ 56 чанков (не byte-identical к input-est) + fresh↔resume детерминизм.
  • Пин: мега-предложение → OversizedSentence=true флаг (синтетика).
  • Пин: глава > EditCeilingOut → абзацный сплит (never-split-paragraph, tiling lossless).
  • Пин (ревью-2 F8, испр.): max_tokens/Reserve edit-единицы сайзятся от ПОЛНОГО ЧЕРНОВИКА единицы (prev, EstimateTokens(prev)·MaxOutputRatio + флор D24.3, D2.5) — НЕ от источника/est_out; синтетика: крупная глава, переданная целиком как prev, получает unit-масштабный max_tokens без ложного length-ретрая.
  • Golden re-capture: снос STMDepth/OverlapTokens + фертильность-бюджет = маскированный дифф старого↔нового снапшота (только хеши/версии + новые поля).

(е) Открытое / маршрутизация

  • logical-стратегия — СЕАМ, наполнение парковано (Q1). DraftBudgetOut/EditCeilingOut — tunable, ревизия по пере-прогону. src→dst редакторский мэппинг — решён здесь (в план), не хвост.
  • Бюджет whole-chapter Select для W2-инъекции редактора (ревью-2 пост-верифай, открытый): свежий Select по ВСЕЙ единице над enriched-банком (§1(б)/F3) не имеет пиннутого token-бюджета; per-чанк GlossaryTokenBudget на всю главу (много больше матчей) может вытеснить approved-канон и разбавить enriched-энфорс. Бэкенду: per-глава бюджет ИЛИ unbounded для констрейнт-блока редактора (решить в пак-2).

§3. WS3 — Банк-машинерия W1.5 в Go (слой 4)

Go-порт детектора V-C (WHICH) + алиас-ярус-1 + консолидация §C2 + эмиссия сид-дельты. WHAT (dst) = НЕ ко-оккуренция (canon-recovery 0.200.68 < 70%) → dst через банкноту (WS4) + Палладий (имена) + облако (термы Z1/Z3).

(а) Что меняем (file:line)

  • новый backend/internal/pipeline/miner*.go — детектор V-A→V-B→V-C (авторитет = exp16 arms.py FROZEN, НЕ пересказ research/20 §B); алиас-ярус-1 (alias.py R1R4); консолидация (canon.py §C2); эмиссия сид-дельты.
  • memseed.goloadGlossarySeed (:64-186) читает BASE-сид + сухую валидацию дельты (штампует Source:"seed" :131); но ПЕРСИСТ майненой дельты после подписи — ОТДЕЛЬНЫМ mined-write-путём (штамп Source:mined, зеркало rubyToCandidates :314-360), НЕ через loadGlossarySeed (F2, §1(в) — иначе Source:seed двигает snapshot_W1); новый tmctl seed-lint (сухой прогон фейл-лаудов по дельте: :87 src, :94-98 dup, :104-108 status, :113-115 draft/approved-dst, :161-181 спойлер-оверлап, :201-237 shared-key).
  • store/glossary.goGlossaryEntry.Source (:36, сейчас seed|ruby|auto) += mined; единая схема кандидата ruby↔mined (research/20 §E-бэкенд-5).
  • store/ruby.go-паттерн — прецедент rubyToCandidates (memseed.go:314-360: Source:ruby, status:auto, no dst, Confidence=occ) зеркалится для mined-канала.
  • артефакты детектора — контраст-словарь (jieba SHA 7197c321…, вне git, воспроизводим из jieba 0.42.1 dict.txt), паттерн-инвентари (PACK_VERSION="zh-universal-v1"), Палладий-таблица (~400 слогов, генерируемая).

(б) Алгоритм (frozen miner-v1 = exp16 §1.2 арм A3; ТОЧНЫЕ пороги)

Детерминизм-контракт: все ранкинги сорт key=(-score, -freq, src); occurrence-count перекрывающийся (pos=i+1), suppress-free (D24.2 — subsume чистит ТОЛЬКО ранжирование); norm = normalizeSourceKey (memnorm.go, trad2simp→ NFKC→lowercase); Contrast-коллизия → MAX freq; Laplace (cf+1)/(total_char+|distinct|) идентичен в char_indep_rel и detect_formants; Палладий-сегментация жадная longest-first, ъ отбрасывается.

  • V-A: кандидаты = Han-n-граммы 16 (БЕЗ сегментации); freq_floor=3; c-value nested-дисконт с g(L)=log₂(L+1) (НЕ log₂(L) — иначе 蛊/转 зануляются, катастроф-скрин FAIL, 蛊 rank 4753); weirdness = p_book/max(word_rel, char_indep_rel, 1e-9); score = max(cval,0)·termhood^1.0, termhood=log1p(weirdness); subsumption α=0.80 (дроп a если ∃ длиннее b, a⊂b, f(b)≥α·f(a)).
  • V-B: + spread (Dice ко-оккуренция ru-лемм, pymorphy3) score·(1+λ·spread), λ=0.0 на инъект-черновиках (spread ВРЕДИТ, конфаунд §D1), свип >0 только на cold-start; spread_freq_min=5; dst_variants top=4, min_dice=0.05, co-salience need=max(2, round(0.30·|chunks|)).
  • V-C: + паттерн-каналы (patterns.py): (1) 百家姓-якорь (SURNAMES_SINGLE после .discard(凝) + SURNAMES_COMPOUND 16 вкл. 古月; окно 12 Han справа → name); (2) титул-суффиксы (TITLE_SUFFIX 26, ≤3 Han слева → title) + (2b) ordinal_title (ORDINAL 15 + suffix → 四代族长); (2c) rank/grade (GRADE_PREFIX 甲…癸 / NUMERAL / RANK_WORD 等转阶级重 品段层 → title, ловит 等/转 форманто-невидимые); (3) топо-суффиксы (TOPO_SUFFIX 26 → place); (4) авто-формант (detect_formants: символ c продуктивен если ≥min_partners=3 различных морфем-партнёров И over_rep=p_book/p_gen≥15.0 — над-представленность, НЕ редкость; изолирует 蛊/窍/虫, отвергает 师/花/等); (5) Палладий ru-канал (is_palladius_token + кап-лемма-гейт is_name_lemma → блок «найти»=най+ти; бонус +56.0). Бонусы {name/place:140, title:120, term:80}×pattern_source_weight {surname:1.0, ordinal_title:1.0, rank_grade:1.0, title_suffix:0.9, topo_suffix:0.9, palladius:0.8, formant_suffix:0.6, formant_prefix:0.5, title_bare:0.4, default:0.3} (pw = МАКС по evidence); sub-floor score=bonus+40.0·f·pw. top_k=90 операционная точка.
  • Алиас-ярус-1 (R1R4, alias.py, precision-safe, R4 hard-blocks ПЕРВЫМИ): R4-ii (разный пол, кроме hidden) → R4-iii/v (разный approved dst → разные сущности ДАЖЕ при containment: блок 族长⊂四代族长 [D38 §3], 古月⊂古月方源) → R1 containment (|X|≥2 → extension; компози-гард: long=short+Y, Y∈{title,place,term}∩gt → ФРАЗА не алиас, блок 古月+族长, сохрани 古月+方源) → R2 фамильный якорь → family (не identity; R4-i разные имена) → R3 общий ru-рендеринг → identity (кап-лемма-гейт: Палладий-токен И name-lemma min_syl=2). R4-iv cooccur_same_sentence. Кластеризация union-find по identity-рёбрам, sorted. Метрика — B³ (entity-level), НЕ MUC.
  • Консолидация §C2 (canon.py): канон по ВСЕМ вхождениям (НЕ first-wins, анти-LTCR) × Палладий-конформность (name/place) × полнота падежной леммы. canon_recovery cover_frac=0.5. Майнер по построению НЕ пишет approved (канон-предложение = draft). Пол = 他/她-счётчики evidence, вердикт владельца (D19.3, hidden невыводим). since_ch = первая глава вхождения (авто); until_ch — только человек.
  • Эмиссия сид-дельты §C2-7: YAML в схеме seedTerm (memseed.go:30-57, БЕЗ новых полей): non-seed кандидат с banknote/canon dst → status:draft (dst, ⟨проверить⟩); WHICH-only без dst → status:auto (инертен). Алиасы — поверхностями терма. Evidence/Source-класс/зоны Z1Z5 — в сайдкар-карту подписи (прецедент diag/glossary_v2_signoff.md), НЕ в схему сида. Пре-валидация против фейл-лаудов loadGlossarySeed = часть карты. Эмиттер обязан реюзить subsumption+fragment-фильтры (emit_owner_sheets.build_precision30: тип∈{name,place, title}, freq≥5, src∉subsumed, len≥2, не фрагмент по PARTICLE-сету), а НЕ сырой 13618-дамп (WS3 (г) находка: сырой дамп несёт §B2-фрагмент-шум).
  • Плагин-сеамы P1P6 (§B5): данные/конфиг per язык/пара (идиом промпт-паков PromptPathFor), НЕ ветки кода. Наполнение — zh(-ru) инстансы + существующий ruby-канал ja. Политика: degrade-with-flag (P2P6, потеря recall видима в карте подписи), fail-loud ТОЛЬКО P1 (нет генератора кандидатов → майнинг для языка не заявляется). P5 = лемматизатор цели (см. ru-лемматизация ниже). P4 = таблица транслитерации по ПАРЕ (Палладий zh→ru).
  • ru-лемматизация (§B5 P5) — Go-прод-путь W1.5 (ИСПРАВЛЕНО ревью-1 F2, прежняя формулировка «λ=0 → лемматизатор не нужен» ОПРОВЕРГНУТА): λ=0 глушит spread-МНОЖИТЕЛЬ, НО dst_variants считается БЕЗУСЛОВНО λ (arms.py:103 _dst_of при freq≥5), а Палладий-канал V-C потребляет dst_variants[:2] + is_name_lemma (arms.py:124-131) — обе pymorphy3-backed (spread.py:45-60,117-143). ⇒ Палладий-подканал V-C НУЖДАЕТСЯ в лемматизации даже при λ=0. Три варианта (owner/бэкенд-развилка §10): (A) faithful pymorphy3-порт (dst_variants + is_name_lemma) → Go воспроизводит golden ТОЧНО, вкл. Палладий-бонус +56.0 → 古月:21; цена — нетривиальный порт лемматизатора; (B) дропнуть Палладий-подканал в Go-дефолте古月 теряет +56.0, ранг 21→22 (катастроф-СКРИН ∈top-50 ВСЁ ЕЩЁ PASS, но exact-ранг не воспроизводится); (C) surface-аппроксимация (cap-предоминантность по поверхности + стеммер для dst_variants) — грубее, паритет НЕ exact. Рекомендация (обновлено ревью-2 к ратифицированному §10-14 дефолту): дефолт (B) — дроп Палладий-подканала с явным пере-пином фикстур (古月:21→22, скрин PASS; dst доставляет банкнота, exact-ранг НЕ продуктовая ценность); (A) faithful pymorphy3-порт — ТОЛЬКО при готовой поддерживаемой Go-морфологии. omission-бэкстоп слоя 5 (WS2/WS5) — отдельно: substring- матч по decl.forms+dst + лёгкий Snowball/Porter-ru-стеммер (Go-либа, детерминирован), pymorphy3 НЕ требуется там.

(в) Конфиг + снапшот

  • Пороги miner-v1 = СТАРТОВЫЙ версионируемый конфиг mining-v1 (свип ±50% инвариантен — A3@f≥3=1.0 держится по floor/over_rep/subsume/partners). PACK_VERSION="zh-universal-v1".
  • Майненые auto/draft строки: при PostcheckGate=false НЕ двигают MemoryVersion (memory.go:264-266); подписанные approved → двигают snapshot_W2 (§1 (в)). Строка манифеста §8.
  • Source:"mined" — новое значение enum (аддитивно, миграция store).

(г) Верификация ДО стройки ($0, несущая) — ws3_miner_verify.py + ws3_seeddelta.py, РЕЗУЛЬТАТ

  • (г) до-стройки: Python miner-v1 на запинённых входах exp16 (§1.10 SHA: 8 код-файлов , jieba 7197c321, сид 175a67ad, records 3de49eaf) воспроизводит golden: катастроф-ранги {方源:0,蛊:1,蛊师:2,古月:21} EXACT, 13618 кандидатов EXACT, recall@proposed full 0.9649≈0.965, test-half 0.9318≈0.932, top-1 方源 score 6640.9. ⚠ Паритет-фикстура РАСЩЕПЛЕНА (ревью-1 F1) — нельзя пинить 古月:21 exact И дропать pymorphy3: (i) Палладий- ИНВАРИАНТНЫЕ гарантии (Go-порт обязан воспроизвести при ЛЮБОМ ru-лемм-решении): recall@proposed 0.965/0.932, 13618-кандидат-СЕТ (членство), катастроф-СКРИН ∈top-50; (ii) Палладий-ЗАВИСИМЫЕ ранги/скоры (古月 rank/score, прочие +56.0-кандидаты): пинить exact ТОЛЬКО при варианте (A) faithful pymorphy3-порт; при (B)-дропе — пере-пин (古月→22, скрин PASS). 方源/蛊/蛊师 (ранги 0/1/2) Палладий НЕ трогает → инвариантны.
  • Юнит-фикстуры алиас-правил (exp16 §3.2): 族长≠四代族长 блок, кап-лемма-гейт «найти», компози-гард — в alias.py (recall известных 2/2). Прогон против Python-реализации — часть фикстур порта.
  • Эмиссия сид-дельты §C2-7 (exp16 её НЕ эмитил — только сайдкары): 61 терм (21 draft+banknote-dst / 40 auto-без-dst); сухой линт против фейл-лаудов memseed = 0 проблем (loadable). Артефакт books/gu-zhenren/design11/ws3_seed_delta.yaml. Находка: draft-кандидаты флагают ТОЧНЫЙ D38-дефект (古月族长→старейшина, 甲/丙-грейд) как draft ⟨проверить⟩ для подписи — дисциплина «майнер не пишет approved» РАБОТАЕТ; auto-кандидаты несут §B2-фрагмент-шум → эмиттер обязан реюзить subsumption+fragment-фильтры.
  • (д) исполнение самого паритета Go↔Python закрепляет бэкенд после стройки (метод mem_select.py в обратную сторону: Python = референс; при расхождении чинить Go-порт).

(д) Тест-спека ПОСЛЕ стройки

  • Байт/значение-паритет Go↔Python miner-v1: Go-порт воспроизводит Палладий-ИНВАРИАНТНЫЕ гарантии (13618-СЕТ, recall 0.965/0.932, катастроф-СКРИН ∈top-50) — фикстуры (г). Палладий-ЗАВИСИМЫЕ exact-ранги (古月:21) — только при варианте (A) pymorphy3-порт; при (B) — пере-пин. Начать с normalizeSourceKey (реюз memnorm.go, уже байт-верифицирован через mem_select.py).
  • Юнит-тесты алиас-правил (5 R4-блоков + компози-гард + кап-лемма-гейт).
  • tmctl seed-lint: прогон эмитированной дельты через РЕАЛЬНЫЙ loadGlossarySeed (не Python-зеркало) → 0 фейл-лаудов.
  • Пин Source:mined (ревью-2 F2): reseed W1.5-дельты штампует Source:mined (НЕ seedloadGlossarySeed хардкодит Source:"seed" memseed.go:131); проверить base-bank-version = хеш Source∈{seed,ruby} СТАБИЛЕН после вливания mined-дельты (иначе snapshot_W1 двигается → «переоплата ОДНА» сломана).
  • Детерминизм: повтор Go-майнера байт-в-байт (без time/rand).

(е) Открытое / маршрутизация

  • λ-режим (spread-коэф): дефолт 0 (инъект); свип на реальных не-инъект черновиках пере-прогона (§10).
  • ru-лемматизатор P5 — полный pymorphy3-паритет парковано; дефолт-майнер обходится (см. (б)).
  • Мини-голд алиасов владельца (§E-1) — recall кластеризации; без него ярус-1 только по precision (§10).
  • precision@30 non-seed A3 — адъюдикация владельца (§10).
  • Z1-адъюдикация → ОБЛАКО (локальная 9B wrong-catch 0.333, принимает 族长→«старейшина»), НЕ локаль. Локальный 9B-споттер НЕ строим (код 0.965 ≥ локаль 0.895).

§4. WS4 — Банкнота: dst-канал (слой 4, wire-инвариант)

Спека banknote-v1 = research/20 §B3 / exp16 banknote.py: разделитель ⟦TM-BANK-v1⟧, ≤MAX_LINES=12 строк src<TAB>dst<TAB>type (type∈{name,place,title,term}, парсер +nickname), «только НОВОЕ» (нет новых → нет разделителя). Строить канал ТОЛЬКО если §D-правило «сноска оправдана» сработало. РАТИФИЦИРОВАНО D39.10 как dst-канал: parse_fail 0% (<5% ✓); ниша = ПРЯМАЯ доставка dst (переводчик эмитит 蛊→гу, non-seed 龙公→Лун Гун — ровно то, что ко-оккуренция извлечь НЕ смогла), НЕ recall (маржин f<3 +1 терм ~10 п.п. — на пороге; код сильнее: f<3 8/10 vs банкнота 5/10); Δкачества mean 0.935 под шум-полом 0.261 → уверенной деградации нет. Цена ~$0.010.1/ранобэ.

(а) Что меняем + 12 интеграционных точек (file:line, HEAD, каждая с тест-спекой в (д))

  1. Санитайзер (sanitizer.go v6): на draft-стадии НЕ гоняется (isFinal-only, chunkrun.go:48). ⚠ Существующий trailingNoteRE (:240) НЕ ловит ⟦TM-BANK-v1⟧/src<TAB>dst-строки (только «Примечание/Сноска»-заголовки), а CJK-leak (:76-84) фаерит только на голых Han-ранах — смешанная банкнот-строка «蛊 гу term» может НЕ сработать (ревью-1 F3: НЕ полагаться на trailing-note как бэкстоп). ПЕРВЫЙ и единственный надёжный рубеж — телеметрия п.10 banknote_parse_fail ДО редактуры. Если нужен финал-бэкстоп — добавить ЯВНОЕ версионируемое ⟦TM-BANK-v1⟧-правило санитайзера (не переиспользовать trailing-note).
  2. Санитайзер cjk_leak (v6): src-колонка = Han по построению → срез ДО санитайзера снимает ложное срабатывание; gloss-whitelist v6 НЕ трогаем.
  3. classify/echo-детект (disposition.go, const cjkEchoThreshold=0.15): считать по ОЧИЩЕННОМУ тексту (срез → classify(stripped)). Порядок ЖЁСТКИЙ.
  4. Coverage-гейт (coverage.go, translator-only chunkrun.go:73): len_ratio числитель по очищенному (сноска ≠ перевод).
  5. max_tokens-сайзинг (stagerun.go:87-100): += бюджет блока (≤12 строк × ~12 ток). Перевод ПЕРВЫМ, блок хвостом → трункация бьёт по блоку; парсер fail-tolerant на последней строке + флаг banknote_truncated.
  6. request_hash/снапшот (render.go RequestHash, snapshot.go): ⚠ (ревью-1 F2) PromptSHA256 (:238) фолдит ТОЛЬКО файл промпта — если инструкция сноски = config-флаг, аппендящий её при рендере (как few_shot), PromptSHA256 не двигается, wire тихо расходится (snapshot.go:110-116-класс). Решение (а): запечь инструкцию в ФАЙЛ промпта (тогда PromptSHA256 реально двигается) ИЛИ явный stage-флаг-фолд. (б) Версия ПАРСЕРА/среза банкноты — verdict-ось (управляет resolved-выходом черновика + derived-hash, как ClassifierVersion :173 / sanitizerSnap :66-77): добавить banknoteSnap{enabled, parser_version} в snapshotID() (единое имя поля, ревью-2 F9-унификация; json-тег в Go — как sanitizerSnap.version, решает бэкенд) — иначе bump парсера без правки промпта оставляет снапшот тем же (свежие чанки режут новым парсером, резюмнутые отдают старый derived → тихая внутри-книжная дивергенция). Включение канала = громкий --resnapshot.
  7. Чекпоинт/резюм (stagerun.go:66-80): resume передоказывает derived-артефакт из сырого чекпоинта бесплатно (content-addressed hash → тот же id).
  8. «Редактор её не видит» (НЕСУЩАЯ, главная правка кода): свежий прогон — редактор читает prev=sr.Text (chunkrun.go:162), хук подменяет на очищенный. НО на резюме OK-путь берёт текст по final_hash, который сегодня = СЫРОЙ attempt (stagerun.go:168-169: if disposition==DispOK { finalHash=last.reqHash }); re-point на derived существует ТОЛЬКО для flagged (:170-185). ТРЕБОВАНИЕ: final_hash OK-стадии draft ПЕРЕНАПРАВИТЬ на tm-banknote-v1 derived-чекпоинт (расширение прецедента flagged→ok). Без этого резюм отдаёт редактору сырой блок. ⚠ Эскалационный путь (ревью-1 F1): draft-стадия ЭСКАЛИРУЕТ (pipeline-c1.yaml:53 escalate_to: deepseek-v4-pro — живое, не гипотеза; pipeline.go:374 разрешает escalate_to только translator). На эскалированном OK-черновике last=esc.fb (stagerun.go:159), finalHash=last.reqHash = СЫРОЙ эскалированный чекпоинт, sr.Text=last.text кормит редактора СЫРЬЁМ. Срез+derived-commit+sr.Text=stripped+re-point ДОЛЖНЫ стоять ПОСЛЕ резолва эскалации (stagerun.go:163) и потреблять last.stripped — тогда эскалированный черновик тоже очищен. (resume.go:32-52 читает GetCheckpoint(final_hash) прозрачно → правка resume.go НЕ нужна.)
  9. Пост-чек глоссария (mempostcheck.go, по финалу редактора): не влияет; но renderGlossaryBlock (memory.go:477) идёт в промпте ДО инструкции сноски («новые = не из этого списка»).
  10. Телеметрия (лауд): per-chunk n_banknote_lines, banknote_parse_fail, banknote_truncated (в retrieval_state- стиле).
  11. Провайдер-квирки: DeepSeek thinking-ON НЕ трогать (models.go echoMineViolation, LoadModels fail-fast). §D-арм меряет дельту качества черновика с/без инструкции (гейт канала; exp16: mean 0.935, под шум-полом 0.261 → уверенной деградации нет, но пере-замерить на пере-прогоне).
  12. Экспорт/полигон: tmctl export отдаёт final без сноски автоматически (срез раньше); кандидаты — из артефакт-файла, не текста.

(б) Алгоритм

  • Срез (split_banknote): idx=output.find(SEP); <0 → (output.rstrip(), ""); иначе clean=output[:idx].rstrip(), block=output[idx+len(SEP):].strip("\n"). Срез ПЕРЕД classifyOutput (chunkrun.go:32) на сыром draft-output.
  • Парс (parse_banknote): строки block.split("\n") непустые; толерантный field-split \t| {2,}|\s*\|\s*; <2 полей → если последняя строка И truncated_generationbanknote_truncated, иначе bad+=1; typ=parts[2].lower() (≥3 полей) иначе term, не-_TYPE_OKterm; src ДОЛЖЕН содержать Han [㐀-鿿] иначе bad; parse_fail=(bad>0).
  • finish=stop-only гейт (обязателен, errata exp16 §6): генерация банкноты принимается ТОЛЬКО при finish=="stop" (не «≠length» — DeepSeek peak-overload даёт insufficient_system_resource). При stop-only truncation-толерантность парсера в проде НЕДОСТИЖИМА (гейт отклоняет не-stop) — но держать в парсере для устойчивости.
  • derived-checkpoint (ТОЧНАЯ формула прецедента commitSanitizedExport, stagerun.go:230-232): sha256("tm-banknote-v1\x00" + reqHash + "\x00" + stripped), id-префикс "tm-banknote-v1:", через PutDerivedCheckpoint (ledger.go:182, cost=0, escalation=0, idempotent), UsageJSON:"{}", FinishReason:"banknote_export". (НЕ упрощённая формула доки без NULL-байтов.)
  • Распарсенные кандидаты → отдельный артефакт-файл (evidence для W1.5 §C), НЕ в store до подписи.

(в) Конфиг + снапшот

  • Включение канала: инструкция запечена в ФАЙЛ промпта (→ PromptSHA256 двигается) ИЛИ stage-флаг-фолд; ПЛЮС новый banknoteSnap{enabled, parser_version} в snapshotID() (verdict-ось, зеркалит sanitizerSnap) — не полагаться на PromptSHA256 (ревью-1 F2). Смена парсера/среза = громкий --resnapshot. Строка манифеста §8.
  • derived-hash неймспейсит версию канала → смена версии = новый id (не коллизия).

(г) Верификация ДО стройки ($0) — ws4_banknote_verify.py, РЕЗУЛЬТАТ

  • Пере-парс 95 строк (14 сохранённых сырых выходов exp16): 95 строк / 65 distinct_src / 0 parse_fail / 0 truncated — EXACT golden (parse_fail 0% воспроизведён).
  • Синтетическая трункация: под truncated_generation=True → 2 годные строки + banknote_truncated (не bad); под Falsebanknote_parse_fail (корректно).
  • fresh↔resume эквивалент-тест (спека): tm-banknote-v1: id = content-addressed (формула с NULL-байтами) → fresh и resume дают ИДЕНТИЧНЫЙ id; смена stripped → другой id. Re-point OK-пути final_hash resume-стабилен конструкцией.
  • Инварианты парсера: SEP не note-слово; non-Han src=bad; no-SEP→всё clean; толерантный сплит (таб/≥2проб/pipe) — все PASS.

(д) Тест-спека ПОСЛЕ стройки

  • Каждая из 12 точек — пиновый тест (срез ДО classify; cjk_leak/coverage/echo по очищенному; max_tokens+блок; derived-hash формула; re-point OK-пути final_hash → derived [fresh↔resume эквивалент]; телеметрия 3 метрики; thinking-ON нетронут).
  • Пин: parse_fail 0% на 95 строках; truncation-толерантность (синтетика); SEP не встречается в чистом тексте.
  • Пин: включение канала = снапшот-сдвиг (промпт-SHA + константа).

(е) Открытое / маршрутизация

  • Дельта качества перевода от форматного констрейнта — пере-замер на пере-прогоне (§11-провайдер §D-арм).
  • Ниша канала = ДОСТАВКА dst, НЕ recall (код f<3: 8/10 vs банкнота 5/10). Z2-дрейф банкнотного dst (转→«оборот» vs approved «ранг») — ловится подписью владельца (draft, не approved).

§5. WS5 — Дефект-класс чекеры + данные пакета пары (слои 5 + 2-данные)

Дефект-класс-леджер D39.8 (все пак/банк/гейт-уровня, НЕ нарезка). Действие при срабатывании: флаг / констрейнт- инъекция — НЕ тихая правка текста (диспозиция D2 цела). Гейты — ДЕТЕРМИНИРОВАННЫЕ ($0): судьи слишком шумны (пол 0.126 когезия / 0.261 смысл — судьи только НОМИНИРУЮТ).

(а) Что меняем (file:line) — по классу: где живёт правило (данные пакета vs код гейта)

  • cheapgates.go — расширяется дефект-классами DC1/DC2/DC7 (детерм. флаггеры на финале, chunkrun.go:199-207 реюз runCheapGates, target-gated isRuTarget, наблюдаемость не гейт по дефолту).
  • новый omission-бэкстоп (слой 5, компенсатор зоны деградации WS2) — reflow-инвариантный лемма-матч.
  • данные пакета пары zh-ru (слой 2, версионируемый) — таблицы конверсии единиц (DC1), негатив-лист регистра (DC6), verse-локусы (DC5).
  • memory.go/renderEditorConstraintBlock — gender-инъекция (DC3) из поля сида Gender (glossary.go:26, memseed.go:35, folded в F1 memory.go:273).

(б) Алгоритм + верифицированный статус ланд-абельности

  • DC-1 时辰/меры (данные пакета + детерм. чекер): таблица конверсии 时辰=2ч, 万/億-порядок, 里/斤/суй-1; извлечь числовое время из src (N个时辰), сверить рендер (三个时辰≈6ч, НЕ «три часа»). Рефайн: флагать ТОЛЬКО явный mismatch (рендер счёта как часов), не «часы не найдены». ⚠ (ревью-1 A1) сработка на 25-гл — ОДНА (n=1, ch1/chunk4), РЕАЛЬНЫЙ дефект (3个时辰→3 часов) на КОРПУСЕ-ОТКРЫТИЯ этого дефекта → «true FP≈0» преждевременно (n=1 ≠ rate). Landing ГЕЙТИТСЯ §5(д)-FP на СВЕЖЕМ пере-прогоне (не текущий rerun). Конверсия детерминирована → низкий-FP ожидаем.
  • DC-2 масштабы 千万/数十万 (b1-класс): 千万=10⁷, 数十万=неск.×10⁵; + word↔word alignment (四成四=44%, 三成=30%, 十二分之一=1/12 — реюз q4a_traps.rule_b1/rule_b2 + LOCATOR). Это то, что regressionguard.go НЕ умеет (арабские цифры только, L5-regressionguard слеп к word↔word). ⚠ (ревью-1 A4) 千万 — стоковая ГИПЕРБОЛА («несметно/тьма»), «тысячи» — in-register литературный рендер, НЕ жёсткая ошибка как DC1/DC6. НЕ bundle с DC1/DC6 как «true FP≈0»; магнитуд-чекер — самый hyperbole-exposed → замерить FP ОТДЕЛЬНО в §5(д). Landing ГЕЙТИТСЯ §5(д).
  • DC-3 gender — РАЗВЕСТИ инъекцию и чекер (ревью-2 F6): (1) Инъекция из поля сида Gender в констрейнт-блок редактора — ЛЕНДИТСЯ для ВСЕХ ТРЁХ состояний, ВКЛЮЧАЯ hidden-мандат: male/female ⇒ жёсткие родовые формы; hidden ⇒ мандат БЕЗРОДОВЫХ конструкций / surface- (→ «он») ДО until_ch (D19.3, 白凝冰) — ЭТО и есть фикс класса Бай Нинбин: инъекция НАПРАВЛЯЕТ редактора, кореференция НЕ нужна. (2) Чекер (наблюдаемость на финале): male/female-чекер ЛЕНДИТСЯ с FP-гейтом §5(д), паратекст-чанки (foreword/синопсис) исключены. hidden-ЧЕКЕР НЕ лендится — ⚠ (ревью-1 A3, УТОЧНЕНО пост-верифай по records.json): FP 3.5% (2 сработки) НЕ обе паратекстные: ch1/chunk0 = паратекст-книга-референс («её»→«эту книгу»), НО ch1/chunk5 («Бай Нинбин… её дар просто пугает», сцена совета старейшин, НЕ паратекст) = РЕАЛЬНАЯ narrative-coref-неоднозначность женской формы на HIDDEN-персонаже — это СИЛЬНЕЕ довод к Ф2, не слабее. Паратекст-исключение снимает ТОЛЬКО chunk0; chunk5 остаётся → нужна РЕАЛЬНАЯ кореференция (→ Ф2). until_ch/sentence-scope НЕ фиксит. ⟶ Не лендится ТОЛЬКО hidden-ЧЕКЕР (инъекция всех трёх — лендится); прежняя формулировка «лендится только male/female энфорс» смешивала — бэкенд не построил бы hidden-инъекцию.
  • DC-4 род говорящего в диалоге: Ф2-annotator (дискурс-уровень, НЕ поле сида — говорящий может быть не в глоссарии). Трап «я мечтал»→«мечтала». Вне детерм.-скоупа слоя 5 → Ф2 (диспозиция §9).
  • DC-5 стих/аллюзии — ⚠ НЕ ЛЕНДИТСЯ детерм. (верификация): loose line-profile детектор ловит трап но FP 73.7%; strict (регулярный метр + поэт-лексема) → FP 42.1% НО теряет позитив. ⚠ (ревью-1 A5) ДВЕ протестированные профили не лендятся (best = 42% FP + потеря позитива; 排比↔стих-неоднозначность трудна); универсального «детекция неспособна» из n=2 не утверждаем. Решение (консервативный дефолт): DC-5 = пак-локусы (владелец курирует известные verse/ allusion src-строки) + рендер-мандат «не сплющивать» + слот сноски + опц. LLM-НОМИНАЦИЯ (не гейт). Продукт-решения (рендер-политика, сноски) — владельцу (§10).
  • DC-6 регистр-лексикон (негатив-лист данных пакета): {терем-класс} — сказочно-русский/канцелярский слой, ломающий жанр. Чекер вхождений на финале + замены. Сработка на 25-гл — ОДНА (ch1/chunk4), РЕАЛЬНЫЙ дефект («терем» в сянься — жёсткая регистр-ошибка). ⚠ (ревью-1 A1) n=1 ≠ FP-rate; Landing ГЕЙТИТСЯ §5(д)-FP на свежем прогоне (негатив-лист по построению низкий-FP). Негатив-лист — данные пакета (расширяемо владельцем).
  • DC-7 editor-инверсии grade/role (сверка на ВЫХОДЕ редактора): канон-грейд 甲乙丙丁→«класс А/Б/В/Г» (не Б/2/A), титул 族长→«глава клана» (не «старейшина»). Точный канон-матч против editor-output → низкий FP ожидаем. ⚠ (ревью-1 A2) DC-7 НЕ имеет $0-верификации (ws5-скрипт не тестирует его) → landing UNVERIFIED, FP только в §5(д) — не утверждать «ЛЕНДИТСЯ» до замера. Корень (D38 §3): promote 甲乙丙丁+四代族长 draft→approved + reseed (owner-only) — иначе renderEditorConstraintBlock (CONFIRMED-only) не инъектит, suppressContained съедает верный 族长.
  • Omission-бэкстоп (слой 5, WS2-компенсатор): reflow-инвариантный лемма-матч (reflow_omission.py): per-source ENTITY присутствует ли ГДЕ-УГОДНО в target (position-independent → reflow-safe); verdict-каскад skip→present→partial →omitted; inflection_gap FLAG (лемма совпала, surface вне pinned decl.forms — НЕ omission, иначе ~7-8% ложных); content-floor ratio=len(tgt)/(1.3·len(src))<0.5 крайний backstop. Правило: entity-omission≈0 = «INSTRUMENT CEILING» НЕ «size safe» (D32.4). Go-путь БЕЗ pymorphy3: substring-матч по {dst, decl.forms, aliases} (детерм.), флаг при отсутствии; inflection-толерантность через лёгкий стеммер (см. WS3 (б) P5-решение). RegressionGuard/ coverage тут смещены (editor легально мержит — research/19 §B0-4).

(в) Конфиг + снапшот

  • Данные пакета пары (таблицы единиц, негатив-лист, verse-локусы) — версионируемый пакет zh-ru, снапшот-folded (правка = громкий resnapshot, как chunkerVersion). Строка манифеста §8.
  • gender-инъекция (ревью-2 реконсиляция): ДАННЫЕ пола (Gender seed-поле) фолдятся через MemoryVersion (правка пола персонажа → снапшот); РЕНДЕР-ЛОГИКА (пол→констрейнт-текст в renderEditorConstraintBlock) фолдится тем же выделенным render-format-version-компонентом, что и src→dst-блок (НЕ memoryMatchVersion — §2(в)/§8).
  • Дефект-чекеры (cheapgates) версионируются StyleCheckVersion (snapshot.go:207, наблюдаемость → resnapshot при правке правила).
  • DC-7 promote 甲乙丙丁+四代族长 draft→approved = reseed → MemoryVersion двигается (снапшот).

(г) Верификация ДО стройки ($0) — ws5_checkers_verify.py, РЕЗУЛЬТАТ

  • (а) Позитивы: 6/6 пойманы (синтетика exp15 §7.9/§2: 三个时辰→«три часа», 千万→«тысячи», 数十万→«десятки тысяч», «терем», стих[loose], gender-hidden). Точные zh-трап-строки (exp14b §2 таблица a1..d2 + 时辰/千万) — фикстуры. ⚠ DC-7 (grade/role) НЕ покрыт $0-скриптом (ревью-1 A2) → его landing verifies только §5(д).
  • (б) FP-fire на 25-гл финале (корпус records.json final; §5(д)-замер = tmctl export после стройки — rerun-store схема v7 < HEAD v8, read-only не мигрирует; exportNormalize косметичен, не меняет слова чекеров): DC1/DC2/DC6 = по 1 сработке (n=1, все на ch1/chunk4 — реальные дефекты, но n=1 ≠ FP-rate, ревью-1 A1) · DC3 3.5% (2 — 1 паратекст book-ref + 1 narrative-coref на hidden-персонаже, A3-уточ; male/female-чекер $0-неверифицирован как DC-7 → §5(д)) · DC5 loose 73.7% / strict 42.1% → НЕ детерм.-гейт. ⟶ Разделение: DC5 НЕ лендится (эскалация владельцу); DC1/DC2/DC6/DC7 landing ГЕЙТИТСЯ §5(д)-FP на СВЕЖЕМ прогоне (текущий n=1 — недостаточно); DC3 — только male/female seed-field.

(д) Тест-спека ПОСЛЕ стройки

  • FP-замер через tmctl export (инвариант №8) на СВЕЖЕМ пере-прогонном 25-гл финале (не records.json): каждый чекер (DC1/2/3/6/7) — измеренный FP-rate в отчёте; чекер с заметной долей ложных не лендится. ⚠ Пред-условие (ревью-1 A6): DC1/DC2 нужен SOURCE-столбец (для 时辰/千万 сверки src↔target); tmctl export --pairs на HEAD НЕ существует (research/20 §E-бэкенд-3) → построить --pairs (source-join в Export уже есть) ДО §5(д)-замера DC1/DC2.
  • Позитивы из эмпирики (exp15 §7.9 / exp14b / exp16) обязаны ловиться (регресс-фикстуры).
  • Sentence-scoping обязателен (q4a_traps F4: скорить ОДНО предложение, эндеры .!?…\n — иначе raw-draft и merged-glm- абзац несопоставимы). Три исхода fix/fail/«?»; «?» ИСКЛЮЧАЕТСЯ из rates, никогда тихий pass.

(е) Открытое / маршрутизация — ВЛАДЕЛЬЦУ (§10)

  • DC-5 рендер-политика стиха + слот сноски (детерм. детекция не работает — эмпирика): продукт-решение владельца.
  • DC-4 род говорящего = Ф2-annotator (диспозиция §9: Ф2-очередь).
  • Промоут 甲乙丙丁/四代族长 draft→approved — owner-only (approved-инвариант).

§6. WS6 — Свап-арм редактора + готовность пере-прогона

(а) Что меняем (file:line)

  • configs/pipeline-*.yaml / models.yaml — армы = КОНФИГ, не код: editor-стадия model: glm-5 (базлайн) / mistral-large-latest / deepseek-v4-pro + few_shot тумблер (pipeline.go:103 FewShot *bool); снапшот per-арм (разный editor-model → разный stageSnap.Model → разный snapshot_W2).
  • export.go/quality.go — атрибуция арма в export/report (editor-model уже в chunk_status/checkpoint).
  • Инвариант: editor pinned (D12, pipeline.go:373: escalate_to только на translator) — свап = смена editor-модели конфигом, НЕ эскалация. Пинится echo_mine_test.go (сейчас требует glm-5 обоих пайплайнов; тест обновляется под арм-варианты).

(б) Алгоритм (прогон-протокол — предпосылки для будущего полигон/монитор-промта)

  • Армы идут ЧЕРЕЗ прод-путь со ВСЕМИ гейтами (урок V2-брака §7.9: eval-риг без прод-гейтов [санитайзер v6 поймал бы 待遇/一步登天] дискредитировал арм).
  • Пред-условие mistral-арма (ревью-2 F4): mistral-large-latest НЕ идёт через прод-путь до пер-модельного rate-guard (§1(б)) — ~48% retry-fails @1.3s без него исказят и латентность, и денежную телеметрию арма. glm-5/ deepseek-pro-армы (grok-семейство 0% retry-fails) rate-guard не блокирует, но семафор общий по конфигу.
  • Инструмент-фиксы слепых пакетов (D39.8 п.3): авто-QA до человека (CJK-в-ru / битые формы / род-консистентность); обрезка ВСЕХ версий по общему финальному предложению; 2-way для тонких контрастов; тир-структура претензий (критические/смысловые/редакторские) вместо плоского «≤2». Планка — 2 претензии (интерим, не пилот Ф2.5).
  • Любое судимое сравнение наследует ратифицированный риг-стандарт (D39.7): per-vote персист, полно-evidence окна (HEAD_CHARS-класс запрещён), оба порядка, floor-гейт маргиналов, DET-primary > судьи где возможен (валидирован D39.9); генерации — только finish=stop.
  • Полы 0.126/0.261 — ИСТОРИЧЕСКИЕ калибровки, НЕ переносимые константы (ревью-2 F5, замечание владельца): измерены в glm/flash-условиях на S2/exp14-материале. Каждый СУДИМЫЙ арм-контраст пере-прогона меряет СВОЙ пол A0↔A0-блоком (~$1/арм) на материале ЭТОГО прогона ДО интерпретации маргиналов — per-editor-model И per-материал. Смягчающее: главные оси floor-иммунны — DET-чекеры (WS5) + слепое чтение владельца (WS6/(д)); судьи только НОМИНИРУЮТ. Внести в предпосылки будущего полигон-промта.

(в) Конфиг + снапшот

  • Каждый арм = отдельный snapshot_W2 (editor-model в stageSnap). Пере-прогон армов = отдельные джобы (не конфликтуют). few_shot:false для reasoning-редактора (deepseek-thinking) — фолдится (snapshot.go:110-116, только где есть ---FEWSHOT--- блок).

(г) Верификация ДО стройки ($0) — ws1_wave_cogs.py (реюз WS1-расчёта), РЕЗУЛЬТАТ

  • COGS per-арм (фертильность × цены providers.json × реальная структура 25-гл, warm-cache/valley): glm-5 $0.335 · mistral $0.205 · deepseek-v4-pro $0.117. Свап-арм editor'ов ДЕШЕВЛЕ базлайна (out $1.5/$0.87 vs glm-5 $3.2) — COGS-win + D38 качество-фикс. Пик-надбавка ×2 (риск-пин) удваивает ТОЛЬКО deepseek-pro-editor до $0.234 → scheduler-aware критичен только для deepseek-арма. Кэш экономит ~17% на editor-input.
  • Качественная сторона свап-арма — эскейп-хатч (эскалация): точная спека «верификация полигоном ПОСЛЕ стройки» в прогон-протоколе (это (д); платная — отдельной полигон-сессией по ратифицированному плану).

(д) Тест-спека ПОСЛЕ стройки

  • Полигон-прогон армов {glm-5, mistral, deepseek-pro} через прод-путь + слепое чтение владельца (тир-структура) — ОТДЕЛЬНЫЙ полигон/монитор-промт по этому плану. Наследует риг-стандарт D39.7.
  • Пин: editor-swap = конфиг (не код); снапшот per-арм; атрибуция в export/report.

(е) Открытое / маршрутизация

  • Качественная ратификация свап-арма — платный полигон после стройки (деньги-развилка §10; кандидат — пере-прогон 310 глав, планка 2 претензии).

§7. Порядок стройки и нарезка на пакеты

Зависимости: WS3/WS4 (банк W1.5) требуют W1.5-стоп-границы из WS1. WS2 (единицы) требуется WS1 (что гонят волны). WS5-omission-бэкстоп компенсирует WS2-зону деградации. Рекомендуемая нарезка:

  • Бэкенд-пак-2 (слой 1 оркестровка + чанкер): WS1 (волновой раннер: W0-precompute, eager-clients, пер-волновой снапшот + wave-sequencing-инвариант, money-под-∥, scheduler-aware) + WS2 (чанкер-бюджет в output-токенах, edit-единица с КОНСЕРВАТИВНЫМ EditCeilingOut≤3200 до Q2a, src→dst редактор, снос мёртвых кнобов). Разблокирует всё (волны — несущий каркас). ⚠ omission-бэкстоп в пак-4 → пока его нет, edit-ceiling держать консервативным (ревью-1 WS2-F1).
  • Бэкенд-пак-3 (слой 4 банк): WS3 (Go-майнер V-C + алиас + консолидация + сид-дельта + tmctl seed-lint + Source:mined) + WS4 (банкнота 12 точек). Гейтится W1.5-стоп из пак-2. WS3-паритет Go↔Python — исполнение в (д).
  • Бэкенд-пак-4 (слой 5 + данные слоя 2): WS5 (DC1/2/3/6/7 чекеры + omission-бэкстоп + данные пакета zh-ru + gender-инъекция) + tmctl export --pairs (ревью-2 F7: пред-условие §5(д)-FP-замера DC1/DC2 — source-столбец). DC-5/DC-4 — эскалация владельцу/Ф2 (не в пак).
  • Конфиг-пак (WS6): армы редактора = yaml/models (не отдельный бэкенд-пак; часть пере-прогонного конфига).

Гейт: каждый пак — мандат самопроверки исполнением (свой код + запросы + результаты); адверсариальный бэкенд-ревью после кода. Затем единый resnapshot (§8) → пере-прогон 310 глав → чтение владельца.


§8. Манифест единого resnapshot (D30.9 — одна переоплата)

Все правки этого плана + уже висящие трек-A компоненты обязаны схлопнуться в ОДИН --resnapshot (одна переоплата книги). Всё, что двигает snapshotID (snapshot.go:101-279):

Уже висящие (трек A залендён, но текущий снапшот книги их не несёт — resnapshot был отложен D38.5/D39.2/39.5):

  • memmatch-v4 (trust-gated suppressor, memory.go:47) → MemoryVersion.
  • sanitizer-v6 (fold-first детекция) → Sanitizer (когда enabled).
  • reseed-сид eb409f2 (промоут 5 термов) → MemoryVersion/BriefHash.
  • editor-v3 0ac5f7a (дискурс+чэнъюй, few_shot-тумблер) → PromptSHA256/FewShot.

Новое из этого плана:

  • WS2: снос STMDepth/OverlapTokens (contextSnap меняется) + segmentationSnap (бюджет-output-токены + фертильность + edit-ceiling) + chunkerVersion bump → снапшот.
  • WS2: src→dst редакторский блок (renderEditorConstraintBlock ФОРМАТ меняется) → выделенный render-format-version folded-компонент (ревью-2 реконсиляция: НЕ memoryMatchVersion — тот const скоуп-матчер/алгоритм memory.go:33-47, формат рендера НЕ ловит автоматически; механизм пинится в §2(в)) → снапшот.
  • WS3: майненые approved-строки (подпись W1.5) → MemoryVersion (только snapshot_W2, §1 (в)); Source:mined enum (аддитивно, миграция store — НЕ снапшот, если не меняет folded content).
  • WS4: включение банкнота-канала = промпт-SHA (инструкция) + константа banknote-v1PromptSHA256 (draft-стадия); + banknoteSnap{enabled, parser_version} (ревью-2 F9, verdict-ось из WS4(в) — управляет resolved-выходом черновика + derived-hash, зеркалит sanitizerSnap; §8 прежде нёс ТОЛЬКО промпт-SHA) → snapshotID().
  • WS5: данные пакета пары (единицы/регистр/verse-локусы) — версионируемый пакет → снапшот; gender-инъекция: ДАННЫЕ (Gender seed-поле) → MemoryVersion, РЕНДЕР-ЛОГИКА → render-format-version (ревью-2 реконсиляция, НЕ memoryMatchVersion; §5(в)/§2(в)); DC-чекеры → StyleCheckVersion; promote 甲乙丙丁/四代族长 → MemoryVersion.
  • WS6: editor-model per-арм → stageSnap.Model (разные армы = разные снапшоты, но это РАЗНЫЕ прогоны, не переоплата одного).
  • Структурное (ревью-2 F9): сама пер-волновая реструктуризация snapshotID() (два MemoryVersion base/enriched
    • два снапшота W1/W2 вместо одного book-global, §1(в)) = снапшот-инвалидация — ОЖИДАЕМАЯ строка golden re-capture (структура хеша меняется даже при идентичном контенте).

Доказательство «переоплата ОДНА»: пере-прогон = ОДИН fresh-translate под финальным конфигом (W1 под snapshot_W1

  • W2 под snapshot_W2); ВСЕ вышеперечисленное фолдится в эти два пер-волновых снапшота ДО старта прогона. W1.5- обогащение банка двигает ТОЛЬКО snapshot_W2 (пер-волновой скоуп, §1 (в)) — W1 не пере-оплачивается. Итог: один draft- проход + один edit-проход = стоимость книги ×1 (не ×N от множественных resnapshot). Golden re-capture: маскированный структурный дифф старого↔нового снапшота = только хеши/версии + новые поля.

§9. Диспозиции хвостов

Свип дорожек L1L8 аудита 08 (полнота; приоритет L1/L2/L3/L5):

  • L1 (контракт t/e): editor-4-мандата-full-regen → WS6 свап-арм (near-term: слабое звено = редактор, D38/D39.9); translator-structure-arm (H3) → ЗАКРЫТ D39.9 (flash-черновик уже верен 25/0/27; pro не ратифицирован; «верность у переводчика» закрыта); diff-editing (H4/E.7) → парковка Q4b (не тестировано; EM 0.680.95 на КОДЕ); fidelity-no- owner → WS5 DC-2/DC-7 детерм. чекеры (полярность/число/грейд/ранг) + omission-бэкстоп.
  • L2 (сегментация): ВЕСЬ трек → WS2 (бюджет-output-токены, edit-единица, снос STMDepth/OverlapTokens, мега- предложение-флаг); фантомный «D35.7» формализован D39 как цель слоя 1 (05-log:563), реализуется здесь.
  • L3 (память): код-корень терм-дрейфа ЗАКРЫТ треком A (d3f6b34 trust-gated suppressor memory.go:684, громкий trustGateEvent) — НЕ переоткрываем; src→dst редактор (D30.1) → WS2 (решён); майнинг ПОВЕРХ → WS3.
  • L5 (экспорт): нормализация ЗАКРЫТА треком A (exportNormalize, fold-first санитайзер v6, CJK-gloss whitelist owner-решение) — НЕ переоткрываем; L5-legit-cjk-gloss-stripped (D38.3 §4б owner-q) → РАЗРЕШЁН (whitelist сохраняет глоссы sanitizer.go:662, WS4-банкнота = dst-канал); резидуалы L5-broken-word (retract) / L5-diacritics (NFC-pass в export) / L5-regressionguard-40% (калибровка) — мелкие долги ниже.
  • L4 (пер-язык промпты; L4-convention-pack-layer NEVER_CLOSED/HIGH): сеам ЗАЛЕНДЁН треком A (prompts:{zh-ru} fail-loud); полный versioned convention-pack ЛАЙЕР + язык-промпта → промпт-тема 10-prompt-architecture.md, гейт exp15+research/18 §D-3 (форма пакета). Субстанция пакета РЕАЛИЗУЕТСЯ здесь: данные пакета пары zh-ru (единицы DC1/ регистр DC6/verse DC5) = WS5-слой-2-данные; плагины P1P6 = WS3. Форма versioned-lay­er — за промпт-темой.
  • H5 (no-in-loop-quality-signal, NEVER_CLOSED/HIGH): частично ЗАКРЫТ треком A (quality.go QualityReport = claim-1 структурный KPI); span-citing судья claim-2 нет. Субстанция слоя-5 сигнала = WS5 (DET-чекеры DC1/2/3/6/7 + omission-бэкстоп = в-контуре измеримость); семантический span-судья — Ф2 (ресёрч-зависим).
  • L6 (дискурс-reflow): Ван Цуй 5 техник + чэнъюй-атом верны; резидуалы (L6-overclaim editor.md:14, deliteralize↔ chengyu tension, chengyu-atom untested) → промпт-тема / измерить в пере-прогоне (мелкие долги ниже). confirm-only.
  • L7 (NEVER_CLOSED владельческие идеи): H6H19 — каждой явная диспозиция ниже.
  • L8 (чистота кода): role-registry / target-гейты / gatesEnabled-снос ЗАКРЫТЫ треком A; остаток (strip-smear 5-файлов, foldModelWire-extract, prompt-orphans) — закрыты пак-1/1.5 (аудит датирован ДО них); confirm-only.

Ledger-очередь D39.4/39.5 (LOW/NOTE — в пак по надобности, не блокеры): golden-глосс-ячейка, escalated+stripped тест, Retries-снапшот pre-existing, пунктуационные огрызки, trustGateEvent первый-отказ, pair-fail-loud-до-store, капы maxGlossCJKRunes=6/maxGlossLatinTokenRunes=24 (tunable, ревизия по пере-прогону), F6-rename (CJK-сплит).

§E research/19 (E.5E.8-бис): E.5 аддитивный Chunk → WS2 (реализовано). E.6 кнобы когезии → СНЕСЕНЫ (carryover не строим). E.7 дифф-apply после Q4b → парковка (Q4b не гонялся; diff-XYZ EM 0.680.95, aider 9198% на КОДЕ, ru-проза — гипотеза). E.8 DeepSeek-слаги → WS1 (в)-(в) (grep). E.8-бис ZAI-кэш → WS1 (в)-(б) (разрешён).

Парковки (НЕ тестировано — консервативный дефолт, НЕ строим, НЕ хоронить как «доказано»; ревизия по пере-прогону): lookahead (Q3b) → WS1 (е). diff-редактор §C2 (Q4b) → E.7 выше. Q4c reflow-отдельным-пассом · Q2c широкое-окно/ узкая-эмиссия · Q5 W2.5-швейный-пасс — все три НЕ гонялись, парк, не в дефолт (§0-скоуп: рычаги сцеплены, но эти конкретные армы вне ратифицированной матрицы; ревизия только если пере-прогон/чтение владельца укажут дефицит).

§E research/20 (E-бэкенд-1…6): E-1 Source:mined+tmctl seed-lint → WS3. E-2 канал сноски → WS4. E-3 tmctl export --pairs (НЕ существует на HEAD, main.go) → ОБЯЗАТЕЛЬСТВО пак-4 (ревью-2 F7: НЕ «слот по надобности» — §5(д) требует source-столбец ДО FP-замера DC1/DC2): source-join в Export уже есть (export.go bookChunks); флаг --pairs строится как ПРЕД-УСЛОВИЕ §5(д)-замера DC1/DC2. E-4 транспорт gender → WS5-DC3. E-5 ruby↔mined схема → WS3. E-6 src→dst редактор (D30.1, memory.go:505-511) → WS2 (решён в план, не хвост).

Дефекты D39.9 (finish=length-гейты в eval-харнесах): exp14_common.call НЕ отклоняет finish=length — это EVAL-риг-долг (полигон), НЕ прод (прод classify обрабатывает length, stagerun.go:455). Прод-инвариант WS4: генерации только finish=stop. Диспозиция: eval-риги будущих полигон-сессий чинят (в промт-стандарт D39.7), прод чист.

NEVER_CLOSED владельческие идеи дорожки L7 аудита 08 (каждой — явная диспозиция, требование 09:165):

  • H7 input-size-limit (лимит входных токенов книги, отдельно от money-ceiling): НЕТ кода/диспозиции. Диспозиция: preflight-чек в W0 (est-токены книги vs конфиг-лимит max_book_tokens, fail-loud ДО трат) — дешёвый, в пак-2 (W0 уже считает est_out). Владельцу: значение лимита (§10).
  • H8 abuse/misuse pre-screen (дешёвый high-precision скрин prompt-injection/non-translation): НЕТ кода/дисп. Диспозиция: паркуется в Ф3 (trust-boundary + injection-probe → eval-очередь, 05-log:311 «не срочно»); дешёвый non-translation pre-screen — записка владельцу (§10: строить сейчас дёшево или Ф3?).
  • H10 user-stop/cancel (+ non-resumable для abuse-джобов): НЕТ кода/дисп. Диспозиция: Ф2/Ф3 (resume.go + ceiling/outage-пауза есть; user-facing stop в tmctl — дешёвый follow-up, но не гейтит пере-прогон). Записка (§10).
  • H16 self-improvement harness (само-улучшение без дорогого human-review): НЕТ кода/дисп. Диспозиция: предпосылка = слой 5 quality-сигнал (WS5) + banknote/bank-auto-build (WS3); сам A/B-loop = Ф3 (frontier-meta- reviewer убит владельцем V4-п2, базовая идея — после того как слой 5 даёт измеримость каждой правки).
  • H17 frontier-meta-reviewer (фронтир-модель ревьюит правки пайплайна): ЗАКРЫТ владельцем (V4-п2)НЕ переоткрываем (ревью-2 F10: без этой строки клейм «H6H19 каждой диспозиция» формально ложен). Базовая измеримость правки = слой 5 (WS5); сам meta-review-loop = Ф3 (см. H16).
  • H6 running-summary (DelTA-class near-term рычаг): carryover НЕ строим (D39.7/8 — метрики под полом 0.126 И читатель). Диспозиция: ретайр для книг этого класса (не хоронить как «доказано» — ревизия на структуро-разнообразном корпусе; §0.1). Не в пак.
  • H9 parallelism («главная V4-идея»): ПОДХВАЧЕНО WS1 (волновой раннер = safe-parallel дизайн; не наивный — клиенты предвычислены, деньги сериализованы, sticky precompute'нут). Диспозиция: строится (пак-2).
  • H15 glossary-auto-build (V4-п4): ПОДХВАЧЕНО WS3 (банк-майнинг W1.5 = query-time context + детектор). Диспозиция: строится (пак-3); broad web-fetch auto-populate = Ф3.
  • H11 backend-render-questions (§D-7 neighbor-reference): PROD-MINOR (MessagesWithInjection принимает произвольную инъекцию); ±1-reference SHELVED (exp14 A-ref null). Диспозиция: закрыто SHELVED (соседи-черновики не в дефолт, §1 (е)).
  • H12 cross-boundary trap-set на вебновелл-домене: не построен; «cross-boundary не доминирует» предварительно (PD- classic). Диспозиция: чистовик на вне-претрейн новелле (после пере-прогона, §0.1).
  • H13 broken-draft-joins (битые склейки без детектора): Диспозиция: omission-бэкстоп WS5 частично покрывает (content-floor); специфичный join-детектор — ledger-хвост LOW.
  • H14 inversion-guard (полярность/ранг/эмоция, D37 §2г): editor-swap = near-term защита (WS6) + DC-7 grade/role чекер + DC-2 полярность-числа (WS5) частично реализуют. Диспозиция: WS5-DC частично + WS6 свап; полный полярность-guard — ревизия по пере-прогону.
  • H18 findings-ledger process (концерн-5, стоящий процесс): Диспозиция: оркестратор-процесс (completeness-critic на границе фаз — этот план = экземпляр; не бэкенд-код).
  • H19 merge↔completeness collision (editor.md:8 сливай vs :9 полнота, без omission-guard): Диспозиция: omission-бэкстоп WS5 = измеримость коллизии (лемма-присутствие после мержа).

L5/L6 мелкие долги (аудит 08 §8 п.9): L5-broken-word-class (fix-or-retract — D-лог рекламирует несуществующую способность; retract в контракте, класс отложен Ф2-морфология) · L5-diacritics-class (NFC-pass дёшев — в export-слой, zero-FP) · L5-regressionguard-40% (калибровать по реальному draft→final распределению — ledger-хвост) · L6-overclaim (editor.md:14 «КОНВЕНЦИЯ русского» → техника-vs-норма split — промпт-тема) · L6-deliteralize↔chengyu (эксплицитная граница — промпт-тема) · L6-chengyu-atom (измерить в пере-прогоне, не считать закрытым).


§10. Реестр вопросов владельцу (консолидированный, с дефолтами плана)

# Вопрос Дефолт плана Развилка
1 W1.5-UX: стоп посреди книги для подписи vs автопродолжение? Явная стоп-граница (раннер стоп → подпись → resume W2) UX-решение
2 DC-5 стих: рендер-политика (не сплющивать) + слот сноски? Детерм. детекция НЕ работает (FP 74%/42%) Пак-локусы (владелец курирует src-строки) + рендер-мандат + сноска; LLM-номинация не гейт. Рекомендация оркестратора (ревью-2): дефолт плана поддержан Продукт-решение
3 DC-3 gender: промоут 甲乙丙丁/四代族长 draft→approved + подпись пола 人祖=male, 古月=n-a, 白凝冰=hidden Промоут + reseed (owner-only, approved-инвариант) Подпись
4 H7 input-limit: значение max_book_tokens? Preflight-чек в W0, лимит tunable Настройка
5 H8 misuse-prescreen: строить дешёвый non-translation скрин сейчас или Ф3? Ф3 (не срочно) Скоуп
6 H10 user-stop: user-facing stop/cancel в tmctl сейчас или Ф2/Ф3? Ф2/Ф3 (ceiling/outage-пауза есть) Скоуп
7 Мини-голд алиасов (~2030 мин): нужен для recall кластеризации ярус-1 Без него — только precision Тачпойнт
8 precision@30 non-seed A3: адъюдикация T/F/? Сайдкар готов (precision_at30.md) Тачпойнт
9 DeepSeek пик ×2: риск-пин (вторичка, не офиц.); ре-чек 24.07 у катовера слагов. Провенанс (ревью-2 F11): клейм D39.7 «официально» имел провенанс «через владельца» (URL/цитата НЕ пинились); свежий чек 19.07 офиц. прайса = ФЛЭТ → label остаётся РИСК-ПИН до ре-чека 24.07 (там запинить URL+цитату); историч. запись D39.7 НЕ переписывать (D23.3) scheduler-aware вне пик-окон + ×2-бюджет-фолбэк Вендор-чек
10 Свап-арм качество: платный полигон-прогон армов после стройки (планка 2 претензии)? Пере-прогон 310 глав через прод-путь Деньги-развилка
11 ja→ru реплика (P1=ruby-канал) как тест общности §0.1 после zh→ru Отдельный мини-фриз след-за-стройкой Скоуп/деньги
12 PostcheckGate флип (hard-gate) — висит за пере-замером D24.4/D28.1 Дефолт-флаггер (false); пер-волновой снапшот держит обе механики Пере-замер
13 Edit-ceiling размер (ревью-1 WS2-F1): глава-единица до 6.5k out span-омиссия НЕ доказана безопасной (только Q2a) Консервативный EditCeilingOut≤3200 до Q2a; большая-глава-арм — платный Q2a span-судья. Рекомендация оркестратора (ревью-2): дефолт 3200 + платный Q2a-гейт поддержан; Q2a ОБЪЕДИНИТЬ с полигон-прогоном свап-армов (Q10) Деньги/качество
14 WS3 ru-лемматизатор (ревью-1 WS3-F2): Палладий-подканал V-C нужен pymorphy3 (dst_variants+is_name_lemma) (A) faithful pymorphy3-порт → golden exact; или (B) дроп Палладий → 古月:21→22, скрин PASS, пере-пин. Рекомендация оркестратора (ревью-2): дефолт (B) — дроп подканала с пере-пином (dst доставляет банкнота, exact-rank НЕ продуктовая ценность); (A) только при готовой поддерживаемой Go-морфологии Порт-цена/точность

§11. Мандат самопроверки (исполнено) + приёмка

Ревью исполнением: каждый верификационный скрипт (eval/design11/ws{1,2,3,4,5}*.py) ПРОГНАН, результат воспроизводим (детерминизм: без time/rand в money-инварианте; фикс-mock-латентности); каждое несущее file:line сверено с ЖИВЫМ кодом HEAD 766a60e (не worktree-память отчётов). Находки верификации, изменившие план: (1) WS2 классиф.-mismatch = 0 на zh (не realign, документировать); (2) WS3 сид-дельта флагает D38-дефект как draft (дисциплина работает) + фрагмент-шум (эмиттер реюзит фильтры); (3) WS5 DC-5 стих НЕ лендится детерм. (74%/42% FP) → эскалация владельцу; (4) WS1 W1.5-re-bill-форк → пер-волновой снапшот.

Адверсариальный селф-ревью несущих алгоритмов — ИСПОЛНЕН (раунд-1, 5-агентный refute-by-default воркфлоу, author≠ reviewer; каждый агент пере-исполнил верификации + сверил file:line на HEAD). Все скрипты воспроизвелись байт-в-байт. Несущие расхождения ВПИТАНЫ в план (помечены «ревью-1» in-line):

  • WS1-F1 (несущее): «переоплата ОДНА» держит НЕ снапшот, а волновой инвариант секвенирования (W2 не пере-рендерит draft; редактор читает черновик из W1-чекпоинта) — renderGlossaryBlock инъектит майненые draft-строки, так что наивный пере-рендер draft над обогащённым банком пере-оплатил бы W1. Снапшот избегает resnapshot-СТОПА. → §1(в) переписан.
  • WS1-F3/F4/F5: money-границы ПАРАЛЛЕЛЬНЫ (≤N in-flight, overshoot ≤ Σ(costest)); mock ≠ полное зеркало (day-ceiling/ идемпотентность в Go-тест); wall-clock = compute-only, end-to-end + W1.5-стоп. WS1-F6/F8: client() read-only в волнах; waves.workers (не fanout).
  • WS2-F1 (риск): edit=глава при 6.5k out НЕ доказана span-безопасной (backstop entity-уровня слеп; только Q2a) → EditCeilingOut≤3200 консервативно + Q2a-гейт (§10-13). WS2-F2: 1797 не byte-identical (4 чанка, 2 абзац-сдвига). WS2-F3: классиф-инвариантность корпус-специфична (не ja/ko). WS2-F4: src→dst — одно несущее обоснование (омонимы), явный снапшот-фолд формата.
  • WS3-F1/F2: паритет-фикстура расщеплена (Палладий-инвариант vs -зависимый 古月:21); «λ=0 → лемматизатор не нужен» ОПРОВЕРГНУТ (dst_variants безусловен λ) → развилка A/B/C (§10-14).
  • WS4-F1/F2/F3: point 8 + эскалационный путь (draft эскалирует в prod); парсер-версия = verdict-ось снапшота (не PromptSHA256); trailing-note НЕ бэкстоп банкноты.
  • WS5-A1..A6: DC1/2/6 сработки = n=1 на корпусе-открытия (НЕ FP-rate) → landing гейтится §5(д); DC2=гипербола-exposed; DC3-fires = паратекст-кореф (не hidden; только male/female лендится); DC5 «две профили не лендятся» (не «неспособна»); DC7 без $0-верификации; §5(д) нужен source-столбец (tmctl export --pairs).
  • Completeness: Q4c/Q2c/Q5 явные §9-строки; H5/L4-pack/L5-gloss названы. CONFIRMED без правок: eager-build race-фикс, Select-детерминизм, derived-hash формула, strip-ordering, records.json-FP-суррогат легитимен.

**Ревью-2 оркестратора (19.07) — ВПИТАН (in-line маркер «ревью-2»; провенанс: соло-проход оркестратора [полное чтение

  • спот-чеки file:line по HEAD исполнением; мульти-агентный рубеж НЕ запускался — лимиты владельца] + 2 замечания владельца F4/F5).** Вердикт: план РАТИФИЦИРУЕМ после фикс-листа. Ни одна находка НЕ пересматривает алгоритмы или шесть $0-верификаций — все правки ТЕКСТОВЫЕ (скрипты не пере-гонялись; ни одна (г)-цифра не изменилась). Фиксы:
  • F1 (MAJOR): EditCeilingOut дефолт приведён к 3200 (было противоречие «8000» в §2(в) vs ≤3200 везде); 8000 = потолок ГЕЙТНУТОГО крупно-главного арма за Q2a. → §2(в).
  • F2 (MAJOR): явный инвариант — mid-run подпись W1.5 трогает ТОЛЬКО Source:mined; правка seed/ruby = ОТДЕЛЬНОЕ пред-прогонное resnapshot-событие (§8), НЕ W1.5; промоут 甲乙丙丁/四代族长 — в reseed. Пин в (д). → §1(в)/(д).
  • F3 (MAJOR): инъекция W2 специфицирована — СВЕЖИЙ Select по ВСЕЙ единице над enriched-банком, запрет реюза per-чанк W0-memSel (base). → §1(б)/§2(б)/(д).
  • F4 (MAJOR, владелец): пер-модельный rate-guard в волновом диспетчере (пер-вызовный 429 УЖЕ есть httpllm.go:27-29, гэп = N-∥); models.yaml-конфиг, НЕ снапшот-ось; mistral-арм — за rate-guard; заготовка quirks → §12; каветка wall-clock. → §1(а)/(б)/(г), §6(б).
  • F5 (MINOR, владелец): полы 0.126/0.261 — ИСТОРИЧЕСКИЕ, per-editor-model + per-материал; каждый арм меряет свой A0↔A0 (~$1); главные оси floor-иммунны (DET + слепое чтение). → §6(б).
  • F6 (MINOR): DC-3 развести инъекцию (ЛЕНДИТСЯ все три вкл. hidden-мандат) от hidden-ЧЕКЕРА (не лендится, Ф2-кореф). → §5(б).
  • F7 (MINOR): tmctl export --pairs = обязательство пак-4 (пред-условие §5(д)), не «слот». → §7/§9.
  • F8 (MINOR): сайзинг edit-единицы (max_tokens+Reserve) unit-aware (МЕХАНИЗМ исправлен пост-верифай: «от ЧЕРНОВИКА prev, не est_out» — блок ниже). → §2(б)/(д).
  • F9 (MINOR): §8 += banknoteSnap{enabled,parser_version} verdict-ось + строка «пер-волновая реструктуризация = снапшот-инвалидация». → §8.
  • F10 (MINOR): H17-frontier-meta-reviewer добавлен в §9-свип (ЗАКРЫТ V4-п2 — иначе «H6H19 каждой» ложен). → §9.
  • F11 (микро): §10-9 нота провенанса Q9 (D39.7 «официально» = через владельца; риск-пин до 24.07, D39.7 не переписывать). → §10.
  • §10 рекомендации оркестратора вписаны: Q13 (3200+Q2a, объединить с Q10), Q14 (дефолт B), Q2 (дефолт поддержан).

Пост-фикс адверсариал-воркфлоу (7-агентный, 19.07 — тот мульти-агентный рубеж, что ревью-2 не гонял; $0 к провайдерам, все 6 $0-скриптов воспроизведены под eval/.venv [зависимости regex/jieba/pymorphy3НЕ system python]). Вердикт критика: РАТИФИЦИРУЕМ, все 11 фиксов на месте, F1 MAJOR полностью снят, MAJOR-противоречий нет; 6/6 golden воспроизведены байт-в-байт. Несущие находки ВПИТАНЫ (маркеры «пост-верифай»/«испр.»):

  • F8 ПЕРЕПИСАН (был неверен): «сайзить от est_out единицы» — ошибка; stagerun.go:87-90 (D2.5) уже сайзит редактора от ЧЕРНОВИКА (prev, ~1.9× источника), а est_out источнико-производна + чужое токен-пространство → НОВОЙ формулы нет, §8-строки F8 не требует. → §2(б)/(д).
  • F1 ПЕРЕ-СИМУЛИРОВАН at 3200: ратиф. дефолт = 25 глав → 37 edit-единиц (11 делятся, 10 split-хвостов <1k out); «25/25 в 2k8k» был 8000-арм. → §2(б)/(г), ws2_chunk_sim.py ceiling∈{8000,3200}.
  • F2 write-path (грунт-гэп критика): loadGlossarySeed хардкодит Source:"seed" (memseed.go:131) → reseed mined-дельты ОБЯЗАН нести Source-столбец, иначе snapshot_W1 двигается; 甲乙丙丁/四代族长 промоут = элевация в Source:seed (base) → pre-run. → §1(в), WS3 (д).
  • Реконсиляция снапшот-loudness: src→dst-блок + gender-рендер фолдятся ВЫДЕЛЕННЫМ render-format-version, НЕ memoryMatchVersion (const скоуп-алгоритм memory.go:33-47) — §8/§5(в) приведены к §2(в).
  • F6 уточнён по records.json: 2-я DC3-сработка (ch1/chunk5, «её дар» на Бай Нинбин, сцена совета) = narrative-coref на HIDDEN-персонаже, НЕ паратекст — СИЛЬНЕЕ довод к Ф2. → §5(б)/(г).
  • Мелкое: banknoteSnap имя поля унифицировано (parser_version); §3(б)/§0-сводка приведены к дефолту B; whole-chapter Select-бюджет — открытый пункт (§2(е)).

Подтверждающий пас (3-агентный, 19.07 — верификация 6 корректировок на НОВЫЕ противоречия) — ИСПОЛНЕН. Поймал ДВА F1-класс-резидуала (моя реконсиляция сиблингов оставила одну строку устаревшей) — ОБА пофикшены: (1) MAJOR — §8-WS5 gender-инъекция → memoryMatchVersion не был приведён (сиблинги §5(в)/§8-WS2/§2(в) были) → реконсилирован к render-format-version; (2) MINOR — §1(в) предлагал «расширить seedTerm-схему», что противоречит §3(б) «без новых полей» → пиннут ЕДИНЫЙ путь: отдельный mined-write-путь (штамп Source:mined), §3(а)/§3(б)/§1(в) согласованы. Корректировки 1/2/5/6 подтверждены ЧИСТЫМИ (F8 grounded stagerun.go:87-90; 37-единиц воспроизведён байт-в-байт; F6 по records.json; имена/дефолт-B). После фикса резидуалов — MAJOR-противоречий не осталось.

Приёмка (оркестратор): пере-исполнение симуляций; спот-чек file:line; refute-воркфлоу по несущим алгоритмам; сверка полноты против скоуп-листа D39.11. Мин-планка ратификации: все WS с исполненной (г) — все шесть исполнены и PASS ($0-верификации воспроизводимы, детерминированы); манифест resnapshot полон (§8); реестр владельцу консолидирован (§10). Гейтнутые армы (ратифицируемы как ГЕЙТНУТЫЕ, стройка сердцевины не ждёт, но арм не стартует до верификации): (1) WS2 КРУПНАЯ edit-единица (>~3200 out) — гейтится платными Q2a span-судьями (сердцевина строит с консервативным EditCeilingOut≤3200); (2) WS6 качество свап-арма — гейтится платным полигон-прогоном после стройки (COGS-сторона (г) исполнена; эскейп-хатч (а)/(б) D39.11); (3) WS5 DC-чекеры landing — гейтится $0-FP-замером §5(д) на СВЕЖЕМ прогоне (текущий n=1 недостаточен — не платный гейт, build-time тест); (4) WS6 mistral-арм через прод-путь — гейтится пер-модельным rate-guard (ревью-2 F4: ~48% retry-fails без него искажают латентность+биллинг). Эти четыре — развилки в §10 (13/10/9/…), маршрутизированы явно, не тихо. Эмпирика exp15/16 — предварительна до вне-претрейн новеллы: несущие пороги «tunable, ревизия по пере-прогону».


§12. Заготовка для 00-provider-quirks.md (ревью-2 F4 — вносит ОРКЕСТРАТОР при лендинге, зона полигона)

⚠ Этот блок — НЕ часть плана-контракта; это черновик записи для docs/experiments/00-provider-quirks.md (зона полигона). Дизайн-синтез-сессия НЕ пишет в docs/experiments/; оркестратор переносит текст ниже в quirks-файл при лендинге плана и там же пинит URL/цитату при пере-замере на прод-тире.

mistral-large-latest — агрессивный rate-limiter (тир-зависим, пере-замерить на прод-тире):

  • Судейский eval-риг (exp15 §7.6 REV.2, N-параллельные вызовы): ~48% retry-fails @1.3s интервала, max latency 64.7s; grok-* в том же риге — 0%.
  • Расширение интервала 1.3→2.6s эффекта НЕ дало (49.5→48.2%) → лимитер похож на токен-бакет / конкуренц-кап, НЕ per-request-пейсинг; тир-зависим.
  • Действие бэкенда (WS1(б)): пер-модельный семафор конкуренции + 429/Retry-After-бэкофф (пер-вызовный уже есть, httpllm.go:27-29,138-140, кап maxRetryAfterWait=5мин). Правило двух направлений: цифры из eval-рига, НЕ офиц. доки — при переносе в quirks запинить прод-тир-замер + вендор-страницу rate-limits.
  • Пред-условие: mistral-editor-арм не идёт через прод-путь до этого guard (§6(б)).