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

1069 lines
140 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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

# План реализации пере-прогонного стека (слои 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 §); 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.go`** `translateChunk` расщепляется на per-волна проходы; sticky-цепочка (`chunkrun.go:107-115`:
`memSel=r.memory.Select(...)` `activeIDs` следующий stickyPrev) precompute'ится в W0.
- **`snapshot.go`** `snapshotID()` (`:101-279`) становится **пер-волновым** (см. (в)).
- **`stagerun.go`** деньги/чекпоинт-путь (`Reserve``SettleWithCheckpoint`, `: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`. Деньги:
`Reserve``Complete``SettleWithCheckpoint` (`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, формат srcdst (решение 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.32.6s эффекта НЕ дало (49.548.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`-строки (промоут draftapproved, правка 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 PASS** `committed==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
(freshresume; 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.go`** `contextSnap` (`: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=1797` → **56 чанков** (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.go`** — `loadGlossarySeed` (`: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.go`** — `GlossaryEntry.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` (НЕ `seed` — `loadGlossarySeed`
хардкодит `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_generation` → `banknote_truncated`, иначе `bad+=1`; `typ=parts[2].lower()`
(≥3 полей) иначе `term`, не-`_TYPE_OK`→`term`; **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); под
`False` → `banknote_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-v1` → `PromptSHA256` (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-судьями (сердцевина строит с
консервативным `EditCeilingOut3200`); (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(б)).