textmachine/docs/architecture/13-tech-debt-anchors.md

252 lines
95 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# Якоря техдолга — справочник к строкам бэклога (НЕ трекер)
> ⚠ **Освежение 04.08 (D39.101):** якоря были входом пакета-чекеров (исполнен D39.78) и арки банка (закрыта D39.69/71); часть находок ниже уже ЗАКРЫТА — закрытия помечены стрелками «→» по месту, счёт закрытий не дублируется в трекер.
> **Единственный трекер — таблица «Бэклог» в `../PROGRESS.md`.** Этот файл — справочные file:line-якоря и скоуп-заметки к её строкам (родился как пост-сверка свипа D39.66, 01.08.2026; вынесен из архива решением владельца — содержимое ЖИВОЕ). Актуализируется лендингами паков, которые трогают эти строки; исполненный пункт помечать ссылкой на D-номер, не удалять.
**Метод:** 4 адверсариальных верификатора (author≠reviewer к свипу) против backend-кода и D-лога; вердикты CONFIRMED_LOST / ALREADY_BUILT / ALREADY_CLOSED / RESCOPE, evidence обязателен. **Итог: 23 пункта — 20 CONFIRMED_LOST · 3 RESCOPE · 0 ALREADY_CLOSED (передумываний не найдено).** RESCOPE вправлены в бэклог тем же лендингом (строки 81/73/49). file:line-якоря ниже — вход для пакета-чекеров (строка 25) и дизайн-пака арки (73).
## Срез: строки 81/83/85 + инфра-дописки
### [RESCOPE] Строка 81 — ja-преп B6-ja: Поливанов-валидатор + kana-омограф POS/known-word гейтинг (D17.1) + чек-лист kana-алиасов сида (D16.4)
Два компонента реально отсутствуют: (1) Поливанов-валидатор — backend/internal/store/migrate.go:186 «translit_policy … field only in v1; validator = Phase 2» и :194 «ruby_reading … bridge to Polivanov dst validation, B6 Phase 2 — NOT a dst»; grep polivanov/Поливанов по backend/*.go даёт только значения конфига в тестах/примерах и комментарий backend/internal/config/book.go:50 — валидирующего кода нет. (2) kana-омограф гейтинг — backend/internal/membank/memory.go:10481051: «KANA and HAN are deliberately NOT boundary-checked … their ≥4 precision is deferred to the kana-precision measurement + the B6 tokenizer»; подтверждено D-логом (D17, тело — слайс `D1-D38`: митигация kana-омографов → «скоуп B6»). НО третий компонент УЖЕ ПОСТРОЕН: чек-лист kana-алиасов сида существует — backend/README.md:38 «## Подготовка ja-книги (чек-лист сида)» с пунктом «Kana-написания имён = matchable … внеси в aliases: руками — иначе „тихо пусто"», а сам механизм D16.4 (ruby-чтение → авто-алиас ручного термина с громким скипом омофонной коллизии) реализован: backend/internal/pipeline/bankmaterialize.go:88-94 (membank.AttachRubyAliasesToManual) — это ровно интерим-фикс из D16 п.4.
**Фикс/скоуп:** Сузить строку 81 до двух реально отложенных работ: «Поливанов-валидатор (translit_policy Phase 2, store/migrate.go:186/194) + kana-омограф POS/known-word гейтинг / B6-токенизатор (memory.go:1048)»; компонент «чек-лист kana-алиасов сида (D16.4)» пометить built (backend/README.md:38 + bankmaterialize.go:92) — в чек-листе остаётся лишь Ф2-хвост «двойные чтения (強敵→とも) полная дизамбигуация», который и так под B6.
### [CONFIRMED_LOST] Строка 83 — F4: бэкап + integrity_check SQLite перед платным прогоном (VACUUM INTO) → **ИСПОЛНЕНО D39.82: `tmctl backup` + pre-flight (`backend/cmd/tmctl/backup.go`), строка 83 закрыта; остаток F4-хвоста — строка 93**
grep -rni 'VACUUM|integrity_check|backup' по backend/cmd/tmctl/ и backend/internal/store/ — 0 вхождений; расширенный grep по всему backend/ — тоже 0. Подкоманды tmctl (backend/cmd/tmctl/main.go:7383) — только translate|report|status|export|redrive|seed-lint, команды бэкапа нет; в internal/store/ (chunkstatus.go, glossary.go, jobs.go, ledger.go, migrate.go, requestlog.go, ruby.go, store.go, voice.go) нет ни файла, ни функции бэкапа/integrity-проверки. SPOF из 06-реестра F4 реально ничем не закрыт.
**Фикс/скоуп:** Не требуется — занесено правильно (малый пак с 67/78, до МАСШТАБА).
### [CONFIRMED_LOST] Строка 85 — пилот-преп добор: Bertalign GOLD-пары (D29.2в) · R2 shadow (D25.2) · exp08 v3 (D21.8)
Bertalign: grep -rni bertalign по eval/ и backend/ — 0 вхождений (нет ни кода, ни данных). exp08 v3: в docs/experiments/ существует только 08-cost-model-v2.md; его шапка (строка 3) прямо говорит «Пере-съём заказан как exp08 v3 (D21.8 … D25.8 …)» — т.е. заказан, не исполнен; в eval/ есть только cost_model_v2.py (v3-скрипта нет). Подтверждается и D-логом (D21): «→ exp08 v3 … до фиксации budget_usd-дефолтов Ф2» — висящее поручение, и 02-mvp-plan.md:34 «пере-съём exp08 v3» как открытый.
**Фикс/скоуп:** Не требуется — занесено правильно (пилот-преп-промт, Ф2.5); дельта уже вписана в docs/POLYGON_PACKAGE4_SESSION_PROMPT.md:4.
### [CONFIRMED_LOST] Дописка к строке 59 — GLM context-overflow гард (finish=model_context_window_exceeded)
backend/internal/llm/provider_openai.go:166171: normalizeOpenAIFinish схлопывает только content_filter-композит и sensitive; «EVERY other value — network_error, model_context_window_exceeded … passes through RAW». Тест фиксирует это как осознанный дефер: backend/internal/llm/pack12_transport_test.go:190 «model_context_window_exceeded … raw (deferred GLM-overflow item)». Ниже по потоку гарда тоже нет: classify() (backend/internal/pipeline/disposition.go:201246) знает ветки decodeErrorFinish/content_filter/refusal/length — model_context_window_exceeded не матчится ни одной: пустой выход упадёт в FlagEmpty («retryable — thinking usually ate the entire budget», строки 230233) — платный ретрай, обречённый при overflow; непустой выход пройдёт как reasonOK (строка 245) — усечённый по контексту текст принят как годный.
**Фикс/скоуп:** Не требуется — занесено правильно (NOT-do пака-12 без решения, полигон-пакет до МАСШТАБА).
### [CONFIRMED_LOST] Дописка к строке 53 — внутрифайловый дубль ключа pair-файла → громкая ошибка → **частично ИСПОЛНЕНО D39.82 (putUniqueDC, строка 53 закрыта); остаток parseCategoryRows — строка 111** (PACK15 §Внутрифайловый)
backend/internal/lang/langpack.go:824 (parseCategoryRows): `cats[f[0]][f[1]] = val` — повторный ключ ВНУТРИ одного файла молча перезаписывает предыдущий (last-wins), без проверки дубля. Громкая коллизия есть только на стыке уже слитого состояния и следующего источника: unionStringMap (langpack.go:466474, «key … is already defined … and the overlay redefines it») — но внутрифайловые дубли схлопываются в parseCategoryRows ДО того, как merge их увидит. Ровно так это и зафиксировано открытым в отчёте: docs/archive/reports/PACK15_STRUCTURE_REPORT_2026-07-25.md:274276 «Дубль ключа ВНУТРИ одного pair-файла всё ещё тихо разрешается „последний выигрывает" … фикс — кандидат следующего пака». Теста на внутрифайловый дубль в langpack_test.go нет (grep duplicate/дубл/already defined — 0).
**Фикс/скоуп:** Не требуется — занесено правильно (мелкие хвосты качества одним паком).
### [CONFIRMED_LOST] Дописка к строке 11 — судьба v1.1 quote-absorption при флипе coverage-гейта (D12-Q1)
Go-гейт стоит на v1 без поглощения кавычек: backend/internal/checks/coverage.go:39 `CoverageGateVersion = "coverage-v1-naive-split-lower-bound"`; grep quote/кавыч/absor/поглощ по coverage.go — 0 вхождений. Python-оракул тоже: eval/refusal_bench.py split_sentences (строка 81) без absorption-логики (grep absorb/quote — 0 содержательных попаданий). Дефер задокументирован в обе стороны: eval/coverage_precision.py:1416 «naive sentence segmentation can undercount RU sentences vs a CJK source (quote-absorbing §3.7 is deferred to v1.1)» и D-лог (D12 Q1) «§3.7 „поглощение кавычек" → v1.1 (координированно)». Гейт при этом выключен (шапка coverage.go: «opt-in (gates.coverage.enabled)») — решение о судьбе v1.1 реально висит на флипе.
**Фикс/скоуп:** Не требуется — занесено правильно (решение владельца при флипе, Python-first лок-степ).
### [CONFIRMED_LOST] Дописка к строке 33 — включить sanitizer в pipeline-c2 при оживлении (D33.4б)
grep sanitiz по backend/configs/pipeline-c2.yaml — 0 вхождений (exit 1), при том что секцию `sanitizer:` несут pipeline-c1.yaml:87 и все три arm-конфига (pipeline-arm-mistral.yaml:47, pipeline-arm-glm.yaml:50, pipeline-arm-deepseek-pro.yaml:51). C2 — спящий скелет (pipeline-c2.yaml:14: «Фаза 0 фиксирует СХЕМУ … исполнение fan-out/селекции — механика раннера Фазы 2»), поэтому отсутствие сейчас безвредно, но при оживлении C2 без правки конфига финал пойдёт без санитайзера — строка корректно держит это как условие оживления. Нюанс цитаты: «D33.4б» как секции в 05-decisions-log.md нет (есть D33/D33.1, строки 409/422; нотация D33.4a встречается в research/18-quality-levers.md:213) — на вердикт не влияет.
**Фикс/скоуп:** Не требуется по существу; опционально при док-пассе поправить ссылку «D33.4б» на фактический носитель решения (серия D33.x в теле D33).
**Сводка среза:** Срез 81/83/85 + дописки 11/33/53/59 D39.66: 6 из 7 пунктов CONFIRMED_LOST — занесены правильно и живы. F4-бэкап (83): 0 вхождений VACUUM/integrity_check/backup во всём backend/, tmctl без такой подкоманды (main.go:73-83). Bertalign+exp08 v3 (85): bertalign 0 вхождений в eval/ и backend/, v3 «заказан» шапкой 08-cost-model-v2.md:3 и не исполнен. GLM-overflow (59): finish проходит RAW (provider_openai.go:166-171, тест «deferred GLM-overflow item»), classify() без ветки — пустой выход = ложно-retryable FlagEmpty, непустой = ложный OK. Внутрифайловый дубль (53): langpack.go:824 молчаливый last-wins, громкость только base↔overlay (langpack.go:466-474), открытость подтверждена PACK15-отчётом:274. Quote-absorption (11): coverage.go:39 = v1-naive, оба слоя (Go+refusal_bench.py) без поглощения, дефер в v1.1 задокументирован. Sanitizer в c2 (33): 0 вхождений в pipeline-c2.yaml при наличии в c1:87 и трёх arm-конфигах. ЕДИНСТВЕННЫЙ RESCOPE — строка 81: Поливанов-валидатор (migrate.go:186/194 «Phase 2») и kana-омограф гейтинг (memory.go:1048-1051 deferred) реально отсутствуют, но третий компонент «чек-лист kana-алиасов сида (D16.4)» уже построен — чек-лист написан (backend/README.md:38) и механизм авто-алиасов реализован (bankmaterialize.go:88-94, AttachRubyAliasesToManual); строку сузить до валидатора+гейтинга.
## Срез: строки 80/82/84 + кандидаты 73
### [CONFIRMED_LOST] Строка 80: резюме-слой памяти (глава→арка→книга) + fact-gate
В backend/internal/ резюме-механизма НЕТ: grep summary/резюме даёт только несвязанное — RedriveSummary (pipeline/status.go:592-593, план redrive), SanitizerResult.Summary() (checks/sanitizer.go:165-167, текст флага), детектор ХВОСТОВОГО edit-summary модели (sanitizer.go:90), коммент-плейсхолдер «approved-glossary+summary+series-bible» в pipeline/snapshot.go:391 при том, что memoryVersion() материализует ТОЛЬКО frozen APPROVED glossary rows (snapshot.go:179-189); в схеме store/migrate.go нет таблицы/колонки резюме (строка 18 — лишь коммент о payload снапшота). Не закрыт решением (D30.8 п.8): «слой авто-памяти/состояния (Annotator, межглавные резюме…) — Ф2, связка с пилотом Ф2.5 зафиксирована» — отложено, не отменено; в списке «ЗАКРЫТО ЯВНО» D39.66 его нет.
**Фикс/скоуп:****Диспозиция ДАНА D39.69: закрыт по нужности; строка 80 = гейт пилота Ф2.5.** (Историческая формулировка: дизайн-пак 73 обязан дать диспозицию «строить в Ф2.5 или закрыть с обоснованием».)
### [CONFIRMED_LOST] Строка 82: морфо-гейт РОДА (C3, глагол прош. вр. при gender=hidden)
В checks/*.go НОЛЬ упоминаний gender (grep gender по backend/internal/checks/ пуст); checks/sanitizer.go:83 и :429 прямо документируют «NOT offline-detectable without a morphology/dictionary pass (Ф2) — left silent»; pymorphy в бэкенде существует только в комментах ОТБРОШЕННОГО pymorphy3-подканала лемматизатора майнера (miner/miner.go:18-23, дефолт B). Gender в банке хранится как TEXT «male|female|hidden|''» (store/migrate.go:183; membank/memory.go:140); hidden даёт лишь ИНЪЕКЦИЮ-мандат избегать род-маркирующих конструкций (memory.go:810-823, GenderHidden-текст), а ДЕТЕКТОРА нарушения нет — membank.SpoilerLeaks проверяет только появление dst-поверхности до окна (mempostcheck.go:205-224), не род глагола. Отложенность зафиксирована 06-memory-risk-registry.md:105 («Отложено в v1.1/Фазу 2 … морфо-гейт глагольного рода (C3)») — жива, не закрыта.
**Фикс/скоуп:** Строка верна как есть (python-сайдкар/pymorphy, связка с морфопроходом Decl из 73).
### [CONFIRMED_LOST] Строка 84: канон D39.21 «generic „гу" = средний род» без носителя
Канон ратифицирован (D39.21; ⚠ прежний якорь вёл в строку карты шапки, которой в живом логе БОЛЬШЕ НЕТ — проверено грепом 24.08: «D39.21 = … generic-«гу» СРЕДНИЙ род, именные — по-термово из банка»), ⚠ **ПЕРЕ-СВЕРЕНО 23.08 (№19): ЧЕТЫРЕ пункта из пяти ПРОТУХЛИ — носитель в коде ПОСТРОЕН.** Было «носителя в коде НЕТ». Стало, по фактам: (1) `genderConstraintNote` принимает `neuter`/`n` и отдаёт `tx.GenderNeuter` (`backend/internal/membank/memory.go:842-850`=`case "neuter", "n":`) — прежний якорь `:815-823` съехал; (2) ключ пары ЕСТЬ`GenderNeuter` в структуре и в разборе (`backend/internal/lang/embedded.go:398`=`GenderNeuter string`, `:456`=`case "gender_neuter":`); (4) `neuter` встречается в `lang/embedded.go`, `lang/data/injection.txt`, `membank/memory.go` и двух тестах — грепа «0 хитов» больше нет; (5) род ВАЛИДИРУЕТСЯ: `knownGenders` включает `neuter`/`n`, а `GenderVocabViolations` ловит выход из словаря, и её собственный докстринг называет этот класс (`backend/internal/membank/memseed.go:542`=`var knownGenders = map[string]bool{`). **ЖИВЫМ ОСТАЁТСЯ РОВНО ПУНКТ (3):** комментарий схемы БД до сих пор перечисляет `male|female|hidden|''` без neuter (`backend/internal/store/migrate.go:184`=`male|female|hidden|`) — и прежний якорь звал `:183`, то есть был на строку мимо. Это косметика комментария, не поведение. Альтернативный носитель decl.Invariant существует (memory.go:128,151,311; seed.go:85), но несёт лишь несклоняемость формы, не род для согласования глагола/прилагательных.
**Фикс/скоуп:** Строка верна; уточнение в тело: (а) сегодня `gender: neuter` в сиде — ТИХИЙ no-op без линта, т.е. дыра хуже «невыразимо» — при стройке носителя добавить и seed-lint-отбраковку неизвестных значений рода; (б) «decl.invariant достаточно» закрывает только несклоняемость, не согласование — фиксировать это в формулировке альтернативы.
### [RESCOPE] Кандидат 73: ranked-set (инъективность рангов)
Половина УЖЕ ПОСТРОЕНА: инъективность dst (B2, «два исходных → один целевой») реализована как membank.InjectivityCollisions (membank/memory.go:1101-1126: группировка approved-строк по нормализованному dst, ≥2 разных src = коллизия) и вызывается на загрузке как warning-only диагностика (pipeline/seeding.go:64). НЕ построены две другие части исходного обязательства (04-unhappy-paths.md:19: «тип ranked-set + проверка инъективности … и порядка ступеней»): типа ranked-set нет (type — свободный текст «name|term|title|…», migrate.go:181; grep ranked-set/tier/ступен по backend = 0 кодовых хитов) и проверки ПОРЯДКА ступеней при перечислениях нет.
**Фикс/скоуп:****Закрыто D39.69 §1 + D39.71 (Q4 не строить).** (Исторически: переформулировать кандидата в 73: «ranked-set: тип записи + проверка порядка ступеней (инъективность dst УЖЕ есть — InjectivityCollisions, warning-only на seeding.go:64; решить, нужен ли hard-режим для ranked-set-строк)».)
### [CONFIRMED_LOST] Кандидат 73: STITCH-ретирование инъекции отработанных термов
Механизма подавления инъекции терма, стабильно отработанного N чанков подряд (research/15-voice-and-state.md:204), в membank НЕТ: бюджет Select ранжирует по priorityRank = confirmed/exact/sticky (memory.go:645-661) без какого-либо счётчика «отработанности»; sticky A5 — ИНЕРЦИЯ в противоположную сторону (наследование в следующий чанк, memory.go:500-505); grep retire/streak/consecutive/отработ по internal/ даёт только loopguard (повтор сегментов) и retired-конфиг-флаги — не о том. Post-check (mempostcheck.go) следит за промахами, но не влияет на подачу.
**Фикс/скоуп:****Закрыто D39.69 (реопен-триггеры: бюджет инъекции / измеренный эффект размера).** (Исторически: кандидат занесён правильно; счётная база для «N чанков подряд без промаха» уже есть в retrieval_state/post-check — стройка сводится к политике селекции.)
### [CONFIRMED_LOST] Кандидат 73: эмбеддинг-второй-эшелон A7/D2 (дать диспозицию)
В ДВИЖКЕ ретривала по эмбеддингам нет: единственные следы — плейсхолдер-колонка `embedding_tier_used INTEGER NOT NULL DEFAULT 0, -- always 0 in v1 (no embeddings on the hot path, Р3)` (store/migrate.go:248) и её протаскивание в store/glossary.go:71,305-391; miner_substrate.go:15 прямо декларирует «$0, no LLM, no embeddings»; grep cosine/vector/embedding по internal/ иных хитов не даёт. В eval бенч существует (eval/retrieval_bench.py). Реестр держит A7/D2 отложенными в Ф2 low-trust (06-memory-risk-registry.md:32,61,105) — живо, не закрыто.
**Фикс/скоуп:****Закрыто по нужности D39.69 (реопен: измеренный recall-разрыв на масштабе).** (Исторически: занесено как «дать диспозицию»; формулировка точна.)
### [CONFIRMED_LOST] Кандидат 73: tmctl «замена термина с главы N»
Команды НЕТ (⚠ перечень в этой улике был неполон и якорь уехал — испр. 22.08: полный список живёт ОДНИМ носителем в коде, `cmd/tmctl/main.go` `dispatchCommands` = `translate·report·status·export·redrive·manifest·backup·migrate·seed-lint`; прежняя редакция не знала `manifest`, `backup` и `migrate`); RedriveSelector выбирает только ФЛАГНУТЫЕ чанки по chapter/chunk/reason (pipeline/status.go:561-582), не «чанки с термом X». Источник кандидата — research/15-voice-and-state.md:200: одна команда ставит until_ch=N старой записи и since_ch=N новой. При этом СУБСТРАТ построен целиком: оконные строки в схеме (UNIQUE (book_id, src, sense, since_ch, until_ch), migrate.go:202) и $0-пере-пин при bank-only-сдвиге (pipeline/repin.go:14-36, D39.42 п.5/D39.45) — платятся только юниты, где инъекция реально изменилась.
**Фикс/скоуп:** Занесён правильно; в дизайн-паке скоупить как ТОНКУЮ CLI-обёртку над уже построенным (правка двух окон в сиде + штатный repin), не как пайплайн-работу.
### [CONFIRMED_LOST] Кандидат 86/73: feed_cap терминолога (D39.45) — «бюджет-гейт режет хвост батча»
feed_cap НЕ построен: grep feed_cap/FeedCap по backend/ = 0 (подтверждено и независимой ревью-шапкой docs/archive/reports/PACK20_BANK_BUILD_2026-07-26.md:14 «feed_cap действительно не построен (grep=0)»). Претензия строки к текущему механизму ВЕРНА и проверена кодом: бюджет проверяется ПЕРЕД каждым вызовом батча и при превышении остальные батчи остаются неконсолидированными status:auto (pipeline/terminologist.go:256-264), при этом кандидаты сортированы ЛЕКСИКОГРАФИЧЕСКИ по src (terminologist.go:191; terminology.go:212), а Batch сохраняет этот порядок (terminology.go:917-919) — т.е. гейт режет лексикографический хвост, не наименее ценное, ровно как констатирует D39.45 (лог:1119).
**Фикс/скоуп:** Занесено правильно (строка 86 + кандидат в 73); формулировка точна.
**Сводка среза:** Срез 80/82/84 + кандидаты 73/feed_cap: 6 CONFIRMED_LOST, 1 RESCOPE, 0 ALREADY_BUILT/CLOSED целиком. Строки 80 (резюме-слой: memoryVersion материализует только approved-глоссарий, snapshot.go:179-189; отложено Ф2, лог:373), 82 (морфо-гейт рода: ноль gender-хитов в checks/, sanitizer.go:83/429 явно оставляет морфологию Ф2) и 84 (neuter отсутствует в enum memory.go:815-823 и в инъекционных текстах embedded.go:395-397; `gender: neuter` в сиде — тихий no-op без линта) занесены правильно. Единственное опровержение — кандидат «ranked-set инъективность рангов»: инъективность dst УЖЕ ПОСТРОЕНА (membank.InjectivityCollisions memory.go:1101-1126, warning-only на seeding.go:64) — RESCOPE на «тип ranked-set + порядок ступеней». По tmctl-замене субстрат построен (окна since/until в схеме + $0-пере-пин repin.go) — не хватает только тонкой CLI-команды. feed_cap: не построен (grep=0), и претензия «гейт режет хвост, не наименее ценное» подтверждена кодом — батчи в лексикографическом порядке src, бюджет-стоп до вызова (terminologist.go:256-264).
## Срез: строка 25 — дефекты чекеров
### [CONFIRMED_LOST] #3 chevron-речь: детектор опознаёт только форму «Реплика», — сказал
backend/internal/checks/cheapgates.go:250-275 — chevronSpeechShape требует закрывающую » СРАЗУ с запятой (строка 263: `if j >= len(rs) || rs[j] != ','`), затем тире (270). Комментарий 248-249 прямо признаёт: «It deliberately MISSES exclamatory «Реплика!» — and unattributed «Реплика.» chevron dialogue». Сверх того, isSpokenChevronLine (checkers.go:603-619) требует глагол речи из закрытого списка speech_verb. Второго, более широкого детектора нет: voice.go:252-253 для кавычечного спана так же требует глагол речи в хвосте. Фаза-2 (D39.64) чекер не трогала — шестёрка закрыла только #11 и translitInterjections.
**Фикс/скоуп:** В пакете-чекеров: расширить attribution-join на «X!», «X?» и безатрибуционные реплики, точность судить по labels/-корпусу (порог Р4-контрактом: потолок recall записать в контракт правила). — **✅ ИСПОЛНЕНО (пакет-чекеров 02.08):** `chevronSpeechShape` — привязка = `«X», —` (запятая-джойн) ИЛИ `«X!»/«X?»/«X…» —` (sentence-final перед »); плоская цитата `«X» —` ОТВЕРГАЕТСЯ (гвоздь `TestChevronCitationNotFlagged` — «просто опциональная запятая» была первой версией, опровергнутой ревью), глагол-гейт держит точность. K4b recall 0.109→0.217, +5 tp, 0 новых fp. **Р4-потолок ~0.22 записан В КОНТРАКТ правила** (24 безатрибутивные реплики офлайн неотличимы от цитат).
### [CONFIRMED_LOST] #5 K5a-предохранитель: number_magnitude отсекает 数-префиксные магнитуды (数十万)
backend/internal/checks/cheapgates.go:508-519 — hasDigitBeforeBigMarker требует ЦИФРУ (一-九/两/0-9) до 万; 数 отсутствует в digit-классе lang/data/cjk-section.txt (digit-строки: 一二两兩三四五六七八九) и не является numeral rune вовсе, поэтому ран рвётся на «十万» и гасится. Комментарий cheapgates.go:482-483 признаёт: «It also drops bare-unit magnitudes (十万/百万) — an accepted recall trade». Нюанс: узкий DC2-проб покрывает РОВНО 数十万→«десятки тысяч» (configs/langpacks/zh-ru/dc-checkers.txt:61-63), но 数万/数百万/数千万 и другие рендеры 数十万 слепы для обоих механизмов.
**Фикс/скоуп:** Ввести «несколько»-коэффициент (数/几) как данные (cjk-section или dc-checkers), снять требование явной цифры для 数X-магнитуд; либо расширить DC2-пробы данными. — **⊘ ЗАКРЫТО (не чинить, пакет-чекеров 02.08):** единственный реальный дефект магнитуды на всём корпусе (`数十万人→«десятки тысяч»`, rerun2-mistral:2:0) УЖЕ пойман DC2 (K5b, recall 1.0). Расширение 数-коэффициента ловит **0 новых единиц** на labels ⇒ нужность = 0. На иных корпусах может понадобиться — текущая эмпирика не оправдывает.
### [CONFIRMED_LOST] #6 латиница-детектор пропускает токены с заглавной буквой (Cultivation, TM-BANK)
backend/internal/checks/checkers.go:527 — `if (rs[j] >= '0' && rs[j] <= '9') || (rs[j] >= 'A' && rs[j] <= 'Z') { reject = true }` — любая заглавная отбрасывает весь токен; 534 — флагуются только !reject. Комментарий 510-513: «flags an all-lowercase Latin token … a capital signals a proper noun/brand». «Cultivation» и «BANK» не флагуются никогда. Recovery битого сепаратора banknote.go:123-138 работает только на сплите ЧЕРНОВИКА (splitBanknote), финальный выход редактора маркер-скана не имеет.
**Фикс/скоуп:** Отдельный класс для Title-case/ALL-CAPS латиницы с allowlist имён/брендов + явный детект bankMarkerFragment в финальном тексте; корректность порога — по labels/. — **✅ ИСПОЛНЕНО (пакет-чекеров 02.08):** `lintLatinResidue` — снят reject-по-заглавной; caps/Title латиница флагуется (ловит `BANK`-фрагмент маркера + `Cultivation`), легит-бренды → per-project allowlist (case-insensitive). K2 recall 0.75→0.85, +4 tp; **измеренная precision 0.971** (1 fp «Qidian»=бренд; 1.0 лишь ЕСЛИ бренд занесён в `style_allowlist` — боевой конфиг сейчас не заносит, находка ревью-агента). Гвоздь `checkers_pack13_test.go`.
### [CONFIRMED_LOST] #7 порог minLatinResidueLen=3 (судьбу решить по labels + Р4)
backend/internal/checks/checkers.go:514 — `const minLatinResidueLen = 3` — порог по-прежнему захардкожен константой, токены ≤2 букв («ok», «ai») никогда не флагуются; никакого data-driven порога или записанного Р4-потолка в контракте правила нет (grep по minLatinResidueLen — единственный сайт).
**Фикс/скоуп:** Замерить распределение длин реальных утечек на labels/-корпусе; порог — в данные цели или зафиксировать 3 как Р4-потолок в контракте правила. — **⊘ ЗАКРЫТО (пакет-чекеров 02.08):** замер THRESHOLD=2 = **0 изменений** (recall 0.75→0.75). Гомоглиф «гy» — это MIXED-скрипт токен (1 латинская руна), не длина-порог; ловится существующим `tk.mixed` (`detectBrokenWords`, приёмка пройдена — гвоздь `TestHomoglyphDetectorAcceptance`). **3 зафиксирован как Р4-потолок** в контракте правила.
### [CONFIRMED_LOST] #10 пост-чек банка: «src сработал» = подстрочное вхождение, однознаковый 转 даёт ложные
membank/mempostcheck.go:12 — пост-чек стоит на посылке «SRC actually fired in THIS chunk»; «сработал» = Aho-Corasick ПОДСТРОКА (memory.go:532). Han-ключи намеренно НЕ проверяются на границы: memory.go:1071-1072 «KANA and HAN are deliberately NOT boundary-checked» (якорь уехал 1048→1071 после банк-пака). Односимвольный 转 попадает в автомат через allow_short (memory.go:152-154) и обходит и мин-длину, и понижение до Ambiguous: collisionProneKey ложен для Han (memory.go:88-90 `!runesAnyHan`), а allowShort гасит ветку (memory.go:653-654 `dispositionFor`). Итог: 转 внутри 转身/旋转 фиксирует «src сработал» → Confirmed-miss пост-чека. 转 живой в холодном банке: COLDRUN_REPORT_2026-07-31.md:143 «转 пришло само (freq 62, „ранг“)».
**Фикс/скоуп:** Дизамбигуация коротких Han-ключей: пер-вхождение с контекстной проверкой (не считать Confirmed-miss без границы/контекста); стыкуется с B6-токенизатором и аркой банка (строка 73). — **✅ ИСПОЛНЕНО пакетом-чекеров 02.08** ([reports/CHECKER_PACKAGE_2026-08-02.md](../archive/reports/CHECKER_PACKAGE_2026-08-02.md); D39.78): `mempostcheck.go` `singleHanKeyFired(p.Via)` — одно-руновый Han-ключ роутит dst-absent-миссы в Unverified observability, не Confirmed (мульти-руновые Han НЕ демотируются). Замер labels: K6 fp 14→6, precision 0.067→0.143, **fn=0 сохранён** (все 6 демотированных = 转 внутри глаголов; ни одного реального промаха не потеряно; ⚠ fn=0 КЛАСС-ВАКУУМЕН — в labels ноль одно-Han-позитивов, recall-цена демоции не измерена, hard-gate строки 12 на него не опираться — D39.78). Демотированный мисс несёт `PostcheckMiss.Demoted` в detail (постфикс §8). Гвозди `mempostcheck_test.go` (вкл. негатив: одно-руновый НЕ-Han остаётся Confirmed). Стеммер-путь `declinedFormPresent` (банк-пак §3) НЕ тронут. Остаточные 6 K6-fp (元石/蛊虫 плюральные формы) = сид-полнота, не код.
### [CONFIRMED_LOST] #1 K5c-супрессор гасит по всему юниту + Р2-семантика «значение» не имплементирована
Обе половины подтверждены. (а) checkers.go:411-412 — «A CORRECT rendering anywhere in the chunk (the ok-suppressor) suppresses the flag»; код 426-435: `!c.qianwanOKRE.MatchString(final)` и `strings.Contains(final, ...)` — весь чанк, не пер-вхождение; одно верное 千万 маскирует второе неверное. DC1 аналогично берёт только ПЕРВЫЙ матч (checkers.go:358 FindStringSubmatch). (б) Р2 (D39.63(1): «жёсткий флаг ТОЛЬКО за неверное ЗНАЧЕНИЕ; неконвертированная форма = мягкий счётчик») не имплементирован: lintPercentScale (checkers.go:482-506) файрит по ФОРМЕ (нет процентного слова + есть дробный кью), pct вычисляется только для сообщения, сравнения значений нет; cheapgates.go:84-89 прямо признаёт: «A correct fix compares the rendered VALUE… a design, not a patch. Left firing». Сплита hard/soft в CheapGateResult нет.
**Фикс/скоуп:** Пер-вхожденческое сопоставление (FindAllString + попарный матчинг) и имплементация Р2: сравнение значения (нужны цель-данные числительных), split жёсткий-дефект/мягкий-линт в результате чекера. — **Р2-split ИСПОЛНЕН (пакет-чекеров 02.08):** `CheapGateResult.UnitScaleHard()`={DC1 ruNum≠2n · NumberMagnitude — слово-магнитуда неверного ПОРЯДКА, ошибка значения (→HARD по находке ревью-агента)} / `UnitScaleSoft()`={DC2 гипербола-неоднозначна, PercentScale верное значение в форме-дроби}; `Total()` не тронут (голден-нейтрально). **Пер-вхожденческий супрессор — ⊘ НЕ строил:** под Р2 PercentScale стал SOFT-наблюдаемостью, локальность-window эвристична и низкоценна → Р4-потолок. HARD-value-чек 成/万 требует ru-numeral-word-данных (потолок записан в v5-блоке шапки cheapgates.go §5-A4 + отчёте; inline-записи на самих функциях НЕТ — хвост в строке 93) — → **§6-Q3 РЕШЁН D39.79 п.4: не строить; реопен = флип hard-gate (строка 12) либо поток value-дефектов с добора идеала.**
### [CONFIRMED_LOST] #8 两 отсутствует в классе cheng_re
configs/langpacks/zh-ru/dc-checkers.txt:65 — `cheng_re ([0-9一二三四五六七八九十])成([0-9一二三四五六七八九]?)` — ни 两, ни 兩 нет ни в одном из классов, хотя тот же пак знает 两: строка 9 `cjk_numeral 两 2`, строка 41 shichen_re содержит 两. 两成 (20%) невидимо для lintPercentScale. По D39.63(3) данные-правки на этой фазе перестали быть дорогими (стендовый корпус одноразовый) — «данные, батчем» в строке 25 корректно.
**Фикс/скоуп:****Подтверждено D39.79 п.3, довершено D39.82 (shichen_re).** (Исторически: добавить 两/兩 батчем данных-правок, resnapshot по дисциплине D39.63). — **✅ ИСПОЛНЕНО как ДАННЫЕ (пакет-чекеров 02.08):** 两/兩 в оба класса cheng_re + 兩=2 в cjk_numeral (парс-полнота). **Нужность на labels = 0** — 两成 в acceptance:10:0/minirun:10:0 замаскировано супрессором #1 (там есть «процент»); правомерно как generality/consistency-фикс. 兩-traditional — §6-Q2 владельцу (0 стоимости, подтвердить или откатить).
### [CONFIRMED_LOST] Резидуал R4 пака-11: DC7 grade/role-чекер · omission-бэкстоп · DC2 word↔word дроби
Определение резидуала: PACK11_CONT_REPORT_2026-07-19.md:195-197 (DC7 grade/role; omission-бэкстоп = «лёгкий Go-ru-стеммер + лемма-матч по {dst,decl.forms,aliases}»; DC2 word↔word дроби q4a rule_b1/b2). Все три отсутствуют: grep DC7 по backend/internal — 0 совпадений (GradePrefix используется только майнером, miner_patterns.go:223 — не чекером); стеммер/лемма-матч отсутствуют — dstFormPresent (mempostcheck.go:144-154) матчит только точные whole-word формы, grep stemmer/lemma по backend даёт лишь комментарии; DC2 в коде = только два проба qianwan/shushiwan (checkers.go:417-438), word↔word-сравнения числительных нет. DC3-гендер из того же R4 корректно вынесен в строку 82 — скоуп строки 25 точен.
**Фикс/скоуп:** Строить в пакете-чекеров: DC7 (после promote 甲乙丙丁 — owner-only), omission-бэкстоп стеммером+лемма-матчем, DC2 rule_b1/b2; дизайны — план §5(б) пака-11. — **Диспозиция (пакет-чекеров 02.08):** (1) **omission-бэкстоп — ✅ УЖЕ построен** стеммером `declinedFormPresent` (лемма-матч {dst,decl.forms}); замер: сам починил 2 K6-fp (凡人/甲等); target-алиасы не заведены (сид-концерн). (2) **DC7 — на владельца (§6-Q1):** approved grade-строки ЕСТЬ (класс А/Б/В/Г), но КОЛЛИЗИЯ с draft «разряд X» — нужна канон-подпись ПРЕЖДЕ стройки (PACK11_CONT:195). (3) **DC2 word↔word — ⊘ ЗАКРЫТО:** 分之-дроби в корпусе есть (8 юнитов), но НИ ОДНОГО размеченного дефекта класса → precision/recall неизмеримы, строить вслепую против мандата; сперва разметить 分之-срез.
### [CONFIRMED_LOST] Alignment-защита рефренов: editor.md велит убирать повторы без исключения «повтор есть в оригинале»
backend/prompts/zh-ru/editor.md:6 — «Правь стиль: убирай кальки…, канцелярит, ПОВТОРЫ, неестественные конструкции» — без оговорки об авторском рефрене; тот же текст в editor-mono.md:5; editor.md:17 дополнительно велит «варьируй лексику». Требование защиты живо: 04-unhappy-paths.md:97 «alignment-защита: повтор в параллельных позициях оригинала = „не трогать“ … иначе редактор-агент убивает рефрены» и :130 п.5. Никакого детектора повторов лемм/alignment-механизма в checks/ нет.
**Фикс/скоуп:** Минимум $0: вписать в editor.md/editor-mono.md исключение «повтор, присутствующий в оригинале в параллельных позициях, сохраняй» (промпт-правка); полный механизм (детектор повторов лемм + alignment) — в пакет-чекеров. — **✅ Минимум ИСПОЛНЕН (пакет-чекеров 02.08):** editor.md + editor-mono.md несут исключение авторского рефрена (двигает боевой PromptSHA256, перекупок нет D39.63; голден на синтет-промтах не двинулся). Полный детектор повторов лемм — НЕ строил (нет размеченного класса; связка с alignment/Bertalign, строка 85).
**Сводка среза:** *(исторический след свипа D39.66 — якоря сводки НЕ актуализировались пакетом (memory.go:1048→1071, checkers.go:527/514, cheapgates.go:263,270 уехали); актуальные якоря — в evidence пунктов выше; срез исполнен D39.78.)* Адверсариальная верификация среза строки 25 (дефекты чекеров, свип D39.66): все 9 пунктов — CONFIRMED_LOST, ни один не опровергнут. Ничего из корзины II пака-21 фаза-2 не строила (D39.64 закрыла только #11 и translitInterjections): #3 — chevronSpeechShape требует ровно «X», — тире (cheapgates.go:263,270, признанные misses в комментарии); #5 — hasDigitBeforeBigMarker слеп к 数-магнитудам (数 нет в digit-классе cjk-section.txt), покрыт лишь узкий DC2-проб 数十万→«десятки тысяч»; #6 — заглавная латиница отбрасывается целиком (checkers.go:527); #7 — порог 3 захардкожен (checkers.go:514); #10 — «src сработал» = подстрока без границ для Han (memory.go:1048-1050), 转 через allow_short обходит все понижения и живой в холодном банке (COLDRUN:143); #1 — ok-супрессор DC2 чанк-уровневый (checkers.go:426-435), Р2-контракт «судить значение» (D39.63) не имплементирован (lintPercentScale файрит по форме, признано в cheapgates.go:84-89); #8 — 两 нет в cheng_re (dc-checkers.txt:65) при том, что пак его знает (строки 9, 41); R4 пака-11 — DC7/стеммер/word↔word отсутствуют в коде полностью (grep — 0 несущих сайтов); alignment-защита — editor.md:6 и editor-mono.md:5 велят убирать повторы без исключения для авторских рефренов вопреки 04-unhappy §7. Формулировки строки 25 точны, RESCOPE не требуется; нюансы (частичное DC2-покрытие 数十万, структурный мин-порог ключей, ограничивающий #10 случаями allow_short/подстрок) — в evidence соответствующих пунктов.
## Срез: строка 86 + дописки 79/41/24/74/49
### [CONFIRMED_LOST] (1) Echo-семантика дропнутого c-lite-члена (строка 86, D39.18): код сохраняет pre-c-lite семантику «эхо где угодно, вкл. дропнутых членов»
Код реально реализует pre-c-lite семантику, альтернатива не построена. backend/internal/pipeline/quality.go:216-218: «echo_draft is counted DIRECTLY from the draft rows (no c-lite per-unit re-derivation — a dropped member's own draft row already carries cjk_artifact)»; quality.go:295-318: draftEcho считается по ВСЕМ draft-stage рядам (FlagReason/FirstFlagReason == cjk_artifact), без фильтра по member-drop — т.е. эхо дропнутого c-lite-члена ВХОДИТ в метрику; backend/cmd/tmctl/render.go:308: «draft = the translator echoed (incl. dropped c-lite members)». Альтернативный путь «echo только в шипнутом тексте» в коде ОТСУТСТВУЕТ (в quality.go нет ветки, исключающей дропнутые члены из draftEcho). D-лог (D39.18): «ОТКРЫТОЕ решение владельцу: сохранена pre-c-lite семантика…; альтернатива — one-line, за владельцем»; D39.66 (строка 1209) повторно числит его открытым. Решения после D39.18 не найдено (grep по голове D39.x).
**Фикс/скоуп:** Не требуется — строка 86 точна: решение реально отложено и живо, код в заявленном состоянии.
### [CONFIRMED_LOST] (2) Magnitude-кап тяжёлой CJK-утечки (строка 86, D38.3 п.4в): сейчас только strip+flag, капа >X%→drop нет
Капа в коде нет. backend/internal/checks/sanitizer.go:770 StripCosmetic — косметический стрип; backend/internal/pipeline/disposition.go:96-104 FlagSanitizerStripped — «cleaned, exported flagged, NOT dropped». detectCJKLeak (sanitizer.go:636) вообще не считает долю утечки — только собирает раны. Единственные share-пороги: disposition.go:275 cjkEchoThreshold=0.15 (это класс ЭХА → FlagCJKArtifact, не кап под-эховой утечки) и sanitizer.go:137 latinHeavyShare=0.25 (Latin-класс, не CJK). Промежуточного порога «CJK-доля >X% (X<15) drop» нет нигде в checks/ и disposition.go. D-лог (D38.3 п.): «альтернатива magnitude-кап (>X%→drop) НЕ реализована — нужен ли кап, за владельцем»; D39.66 повторно числит открытым.
**Фикс/скоуп:** Не требуется — строка 86 точна.
### [CONFIRMED_LOST] (3) lintNumberMagnitude без гейта по письму источника (дописка 79): активен по цель-данным, SourceScripts не читает
Гейта по письму источника нет. backend/internal/checks/cheapgates.go:414-416: единственный гейт — `if c == nil || len(c.magnitudeStem) == 0` — это ЦЕЛЬ-данные (checkers.go:51 «target-general: ru magnitude word stem», checkers.go:159 — из пар-конфига `magnitude_stem`). grep sourceScripts/SourceScripts по cheapgates.go — 0 вхождений: механизм SetSourceScripts существует (checkers.go:104-125) и используется другими путями, но lintNumberMagnitude его НЕ читает. Источник-сторона захардкожена: cjkMagnitudeOrders (cheapgates.go:464-466) ищет CJK-маркеры 万/萬/億/亿/兆 безусловно. На ja-книге чекер активен без гейта (случайно корректен — маркеры общие с ja); на не-CJK источнике инертен лишь по контенту, семантика источника остаётся захардкоженной в общем слое — находка §8 карты D39.60 актуальна.
**Фикс/скоуп:** Не требуется — дописка 79 точна («гейтить по объявленному письму источника», кандидат при ближайшем касании чекеров).
### [CONFIRMED_LOST] (4) Golden-счётчики голоса (дописка 41): в capture.golden voice-счётчиков нет
Подтверждено отсутствие: `grep -c -i voice backend/internal/pipeline/testdata/golden/capture.golden` = 0 (ни одного вхождения); в testdata/golden/pipeline.yaml ключа `voice:` нет (grep пуст) — гейт opt-in OFF по умолчанию (backend/internal/config/pipeline.go:279-281), фикстура его не включает, значит golden не пинит ни n_voice_flags/voice_detail, ни VoiceFlags/SpoilerLeaks (quality.go:269-278, 436 — счётчики существуют в коде, но в golden-контур не заведены). Хвост D39.58 реален и жив.
**Фикс/скоуп:** Не требуется — дописка 41 точна.
### [CONFIRMED_LOST] (5) Слот инъекции голоса (дописка 24): инъекционная половина построена условно и НЕ включена, оба провода
Оба провода подтверждены. Провод 1 (инъекция): половина построена в membank УСЛОВНЫМ фолдом — backend/internal/membank/memory.go:248-252 (`InjectVoice reports whether the voice/address rows reach the WIRE... It is the CONDITION`), 370-377 («A book with no voice rows, or a run with the injection off, hashes BYTE-IDENTICALLY»), 448-465 (фолд под `if !in.InjectVoice`); но pipeline захардкоживает выключенность: backend/internal/pipeline/bankmaterialize.go:291-294 — «InjectVoice is FALSE and has no config knob: pack-19 builds the schema and the flagger, and D21 п.2 holds the injection conditional until the polygon experiment... Wiring the injection means setting it and accepting a full --resnapshot». Провод 2 (флаггер): гейтится Gates.Voice.Enabled, opt-in OFF (backend/internal/pipeline/voicerun.go:46; config/pipeline.go:264-281). Совпадает с D39.55: «слот инъекции НЕ выбран, оба провода, решит полигон-эксперимент».
**Фикс/скоуп:** Не требуется — дописка 24 точна (решение по gates.voice и инъекции — на следующем живом прогоне).
### [CONFIRMED_LOST] (6) Остаток §12.7 (дописка 74): 8 несудимых fidelity-пар P4 существуют в ~/books/gu-zhenren/research15-probes/
Сырьё существует, суд не докуплен (проверено счётом файлов и метаданными, тексты не читались). P4-артефакты: ~/books/gu-zhenren/research15-probes/probes/raw/ — 100 файлов, вкл. полные сетки echo-ds-gu008-{D0,D1,D2,DP}×{s0,s1,s2} (12 шт.) и echo-grok-gu-001..010×{G0..G4} (50 шт.). Судейское сырьё: ~/books/gu-zhenren/promptlang/judge_p4/votes.jsonl = 36 голосов ровно над 16 distinct (fam,arm,frag)-комбо: deepseek только gu008-s2×{D1,D2,DP} (3), grok G1×{001,002,004,005,006} + G2×{001,002,004,005} + G4×{001,002,004,005} (13). Отчёт POLYGON_PROMPTLANG_ANTIECHO_2026-07-31.md:775-776 («Судья остановлен своим капом $0.28 на 16 парах из 24») и :784 («Добрать 8 несудимых пар P4 ≈$0.10»); файлы несудимых комбо (ds-gu008 s0/s1 × 3 арма; grok gu-006 G2/G4 и далее) в raw/ присутствуют.
**Фикс/скоуп:****Строка 74 закрыта D39.95, P4-хвост принят D39.61; добор ≈$0.10 — резидуал §12.7 D39.61 (цены записаны там), отдельного носителя не заводилось.** (Исторически: сырьё лежит, добор отложен «до предмета wiring».)
### [RESCOPE] (7) Протокол гендер-твиста D5.1 (дописка 49): «redrive есть, селективного by-until_ch пути нет»
Посылка «селективного пути нет» ОПРОВЕРГНУТА: селективная пере-редактура по изменению банка + смета ПОСТРОЕНЫ и живьём проверены. `backend/internal/pipeline/repin.go:14-35`=`POINTWISE re-edit by key` — «POINTWISE re-edit by key (pack-20 point 5, D39.42 п.5)»: bank-only сдвиг снапшота + неизменный content_hash → пере-пин юнита за $0, изменённые инъекции — платно; «The same predicate drives the ESTIMATE: projectRebill uses it» (смета «N units, ~$X»); встроено в штатный резюм — stagerun.go:87 (repinnable); согласие на пере-оплату — rebill.go:11-40 (D20.2-Q2). until_ch-апдейт = правка строки банка → двигается ТОЛЬКО memory_version → ровно этот путь (D39.42 п.5: «поздняя подпись и ЛЮБАЯ правка»; D39.45: «точечная ре-редактура по ключу с $0-пере-пином — построено, живьём проверено... 20 юнитов пере-пинены за $0 при bank-only-сдвиге»). При этом верно: tmctl redrive существует (`cmd/tmctl/main.go:229`=`case "redrive":`), но его селектор — только chapter/chunk/reason по ФЛАГНУТЫМ чанкам (status.go:564-582), until_ch-пути в НЁМ нет — селективность живёт в другом инструменте (translate-резюм + repin + rebill), и это by-design правильный носитель.
**Фикс/скоуп:** Пере-формулировать дописку в строке 49: тех-механизм «until_ch-апдейт → селективная пере-оплата затронутых юнитов + смета» УЖЕ построен (repin.go/rebill.go, D39.42 п.5, D39.45) — слова «селективный redrive» убрать (redrive — инструмент флагнутых чанков, не носитель). Остаток дописки — только операторский ПРОТОКОЛ-документ релиза D5.1 (последовательность: выставить until_ch у gender=hidden-терма → tmctl translate → согласие на rebill-смету; + курация 白凝冰 из строки 24), либо закрыть дописку явной ссылкой на D39.45.
**Сводка среза:** Адверсариальная проверка среза (строка 86 + дописки 79/41/24/74/49) свипа D39.66: 6 из 7 пунктов подтверждены как корректно занесённые (CONFIRMED_LOST) с file:line-доказательствами отсутствия в коде: (1) pre-c-lite echo-семантика реально сохранена (quality.go:216-218,295-318), альтернатива не построена; (2) magnitude-кап CJK-утечки отсутствует — только strip+flag (sanitizer.go:770, disposition.go:96-104; пороги 0.15/0.25 — другие классы); (3) lintNumberMagnitude гейтится только цель-данными magnitude_stem (cheapgates.go:414-416), sourceScripts не читает; (4) capture.golden — 0 voice-вхождений, гейт в фикстуре выключен; (5) инъекция голоса построена условным фолдом (memory.go:248-252,448-465), но InjectVoice захардкожен false без ручки (bankmaterialize.go:291-294), флаггер off (voicerun.go:46); (6) 8 несудимых P4-пар — сырьё на месте (research15-probes/probes/raw: 100 файлов, judge_p4/votes.jsonl = ровно 16 судимых комбо из 24). Пункт (7) — RESCOPE: посылка «селективного by-until_ch пути нет» опровергнута — точечная ре-редактура по изменению банка со сметой и $0-пере-пином построена и живьём проверена (repin.go:14-35, stagerun.go:87, rebill.go; D39.42 п.5, D39.45); tmctl redrive действительно не имеет until_ch-селектора, но носитель — translate-резюм+repin. Предложена правка дописки строки 49: оставить только операторский протокол-документ релиза D5.1 или закрыть ссылкой на D39.45. Ничего не менялось, не коммитилось, $0.
## Архео-хвосты жирных строк бэклога (выселение докс-паком строки 167, 09.08.2026)
> Тела ячеек «Хвост» перенесены сюда ДОСЛОВНО на момент выселения (норма D39.125 п.3: «диспозиция сверху, археология вниз»; ячейка >800 байт — не трекер). Живая диспозиция каждой строки — ТОЛЬКО таблица бэклога `../PROGRESS.md`; здесь справочная археология. Закрытия помечать по месту стрелкой «→ Dxx», не удалять.
### Б-13б — тело строки 13б на 09.08.2026
**`speech-cue.txt` zh НЕ шипить до замера голос-флаггера на холодном прогоне** (данные для недоказанного детектора = инерция; вывод обсуждения 31.07): оси пустые → файл не нужен, оси ловят → решение владельца о шипе (байты двигают `LangpackVersion`). ⚠ **Замера ВСЁ ЕЩЁ НЕТ (D39.86):** поверхность вывода построена (строка `VOICE` в `tmctl report`), и живое чтение coldrun-a даёт `flags=0 over attributed=0 of replies=0` — но это НЕ «оси пустые»: флаггер живёт на ФИНАЛЬНОЙ волне (`waverun.go:392,512`), а редакторская волна там не гонялась ⇒ оси не считались НИ РАЗУ. ⚠ И поле `rules=` в новой строке говорит «гейт СКОНФИГУРИРОВАН» (читается из конфига ВРЕМЕНИ ОТЧЁТА, `quality.go:436`), а не «гейт отработал» — честный дискриминатор это ЗНАМЕНАТЕЛИ (`replies=`); читать так до фикса (строка 105)
### Б-44 — тело строки 44 на 09.08.2026
**Флор — калиброванная константа без модели «thinking в completion»: МОДЕЛЬ ПОЛУЧЕНА замером (D39.86), сама строка сужена до выбора числа.** Замер боевой формы: при вендор-дефолте `high` размышление на плотном хане потолком НЕ ограничивается (8496 → 0/9 пригодных, 16992 → 1/5; вендорская норма вывода для `high` = 384K) ⇒ **флор не является рычагом против неограниченного думания — рычаг это `reasoning_effort`**; при `low` размышление 170313 ток. и флор 8000 избыточен. Остаток строки: какое число ставить ПОСЛЕ развилки 74 (и помнить, что у `deepseek-v4-pro` эффорт не настраивается — вендор-маппинг `low→high`, quirks п.3а). ⚠ **РАСХОЖДЕНИЕ ТРЕКЕРА СНЯТО (D39.90):** строка 74 объявляла 44 «закрытой этим замером» — это верно только для ПОЛОВИНЫ «поднять флор» (фальсифицирована). Живой остаток — обратный: **опустить/пере-калибровать число по числам пробы `bank-low`**, где стена на `low` не исчезла, а разредилась (упавший батч: `content=""`, `finish=length`, `completion=7999`, `reasoning=7999` — тот же тяжёлый хвост, 1/5 вместо 4/5, и это стохастика вызова: батч не самый большой по входу). ⚠ **Флор при `low` НЕ варьировался ни разу** — упавший батч умер ровно на потолке 8000, так что «сколько ставить» остаётся незамеренным вопросом, а не выводом
### Б-46 — тело строки 46 на 09.08.2026
**Экран целевого языка на выходе НЕ-ПОСЛЕДНЕЙ стадии — ЖИВОЙ НОСИТЕЛЬ ПОЛУЧЕН, класс поднят (D39.86).** Было: 38/40 дефектов латиницы живут в черновиках вне охвата гейтов (`waverun.go:486`). Стало: на ре-пробе 74 при `effort:low` **1 черновик из 18 пришёл ПОЛНЫМ связным переводом НА АНГЛИЙСКИЙ** и прошёл как `disposition=ok``classify` экранирует ИСХОДНУЮ письменность, пустоту, обрыв и отказ, а «не исходный и не целевой» не ловит ни один предикат (частично ловится лишь третий язык, ДЕЛЯЩИЙ письменность с источником, и ловится под ЛОЖНОЙ причиной `cjk_artifact`). ⚠ Последний рубеж УСЛОВЕН тройкой `isFinal && Gates.Sanitizer.Enabled && checkers.TargetActive()` (`chunkrun.go:52`), и **дыра на оси ЦЕЛИ двусторонняя**: (i) без данных цели слой 7 инертен ПО ОТСУТСТВИЮ (осознанный дизайн, `disposition.go:302-305`) ⇒ для не-русской цели рубежа нет вовсе; (ii) а с данными будущей ЛАТИНОПИСЬМЕННОЙ цели `detectLatinInsertion` (`checks/sanitizer.go:228`) сработает на ЧИСТОЙ целевой прозе и дропнет 100% чанков — внутри санитайзера он НЕ обусловлен `TargetScriptNonLatin()`, хотя доккоммент это объявляет (реальный гард только на репэйре — `quality.go:215`, `repair.go:208`), а частичный файл цели невозможен (`CompileCheckers` паникует). ⇒ **ревью-вопрос «заработает ли пара, которой в репо нет, без правки Go?» для →en/→de отвечается НЕТ**; кандидат фикса — тот же шов, что уже применён к репэйру. *(Контрпример «pipeline-c2.yaml — легальная сборка без санитайзера» проверен и СНЯТ: c2 неисполняем `CheckRunnable`, его санитайзер забукован строкой 33.)* Даже при включённом санитайзере худший исход — не потеря юнита, а ТИХО ОТГРУЖЕННЫЙ релейный перевод zh→en→ru, если билингв-редактор отрендерит русский с английского черновика. Область фикса — «выход любой НЕ-последней стадии», не «черновик» (`isFinal` позиционный, `stagerun.go:145`). ⚠ **ДИЗАЙН ПОДАН, КОДА НЕТ — СТОИТ НА РЕШЕНИИ ВЛАДЕЛЬЦА (D39.90).** Три поправки сессии к посылке, все замером: **(1) боевой гейт уже ловит 18 из 20** off-target событий (`sourceScriptShare > 0.15`, `disposition.go:224,275`; 0 ложных на 1055 здоровых, запас 13×) ⇒ остаточная дыра **2 чанка из ~1100 = 0.18%**, оба английские, японский ловится долей ханьцзы; **(2) второй рубеж существует, но стоит не там** — санитайзер поймал бы те самые байты («Latin insertion»), но `chunkrun.go:52` требует `isFinal`, а в двухстадийной топологии черновик НИКОГДА не финальный; тот же `isFinal` глушит на черновике латинный линт, флаггер голоса и глоссарный пост-чек; **(3)** здоровый минимум на тексте, который РЕАЛЬНО видит гейт (после среза банкноты), = 0.9767 при n=1055. **Развилка владельцу:** (а) предикат внутри `classify()` + бамп `classifierVersion` — двигает снапшот у всех книг, голден пере-захват один раз, на свежем стенде $0; (б) отдельный опциональный гейт вне `classify()` — снапшот не двигает, но это второй способ сказать «ответ непригоден», и он выключен по умолчанию; (в) языковой детектор. **Выбор сессии — (а).** Носитель данных — `lang.LangScripts(Book.TargetLang)`, НОВЫЙ go:embed-файл не заводить (`EmbeddedVersion` фолдится безусловно = перекупка всех книг всех пар); порог 0.50 не калиброван, а поставлен в середину пустого интервала. Три мины наивной реализации названы: новый флаг провалился бы мимо `escalatable()`/`retryable()` (⇒ переиспользовать `FlagCJKArtifact`), классификатор ложно флагался бы 16/16, экран слеп к ПОДМНОЖЕСТВАМ письменности (чисто китайский выход для →ja наберёт 1.000). Вторая половина дыры (`detectLatinInsertion` без `TargetScriptNonLatin()`) — чинить тем же диффом, для ru вердикты не двигаются. **Константы и хвосты дизайна, без которых подпись неполна:** порог **abstain 200 букв** (ниже экран воздерживается; самое короткое off-target событие 346 букв, ниже 200 падают лишь 5 из 1055 здоровых, все со счётом 1.0000) · ⚠ **опубликованная база «~1.5% off-target» НЕВЕРНА по знаменателю** — он пулит черновики с редакторскими выходами, а редактор такой отказ произвести не может; на ЧЕРНОВОЙ вызов база **3.02%**, то есть вдвое хуже · ⚠ **не проверено, что делает РЕДАКТОР с английским черновиком** — вытащит обратно в русский или отредактирует как английский; именно этот неизвестный решает, чем является остаточные 0.18%: потерей юнита или тихо отгруженным релейным zh→en→ru · английских черновика найдено ДВА (`fb51e135a395`, `4080ee8287af`), плюс японский в exp15 · **`cultivation` чинить в СИДЕ, а не порогом:** 86 из 1055 здоровых несут голое английское `cultivation` внутри беглого русского, и все 32 абзаца ниже 0.90 содержат именно его, ни один — ханьцзы; правка пар-данных двигает `LangpackVersion` с `omitempty` ⇒ перекупка ограничена книгами ЭТОЙ пары (чинить на плане, где перекупка уже) · Hunspell и GlotScript-эмбед — **не строить** · база замера = строка **113** · **02.08 вечер (D39.92): владелец пере-поставил задачу closed-set** («вернуло НЕ тот язык, что просили»; полноценный LID — только запасной флаггер вне пути вердикта; проверку изолировать отдельным слоем) — ресёрч §5.15.3 отвечает постановке, форма (а)+изолированный модуль подтверждена, слой-0 владельца (полоса len(out)/len(in)) добавить замером на фикстуре 113
### Б-105 — тело строки 105 на 09.08.2026
**Роль `classifier` мис-вердиктится как эхо + честность двух новых строк наблюдаемости (D39.86).** (а) `classifyOutput` даёт поблажку `SourceEchoExpected` только `roleTerminologist` (`chunkrun.go:37`), а ответ классификатора по формату состоит из исходных ханьских термов ⇒ доля исходного письма 0.200.29 > порога 0.15 на каждом батче: живой артефакт пробы 6/6 несёт `verdict=cjk_artifact` при `bad_lines=0`. Ущерб узкий — `runBankRoleBatches` кладёт `run.texts[i] = att.text` без проверки диспозиции (`terminologist.go:603`), посчитанные эхо-ставки (`quality.go:313-321`) берутся из `chunk_status`, куда бэнк-путь не пишет ⇒ портятся только строки `tmctl report`/лога; **латентно** (`classify_types` выключен во всех шиппинг-конфигах). ⚠ Цена дисциплинированного фикса, которую надо знать заранее: бамп `classifierVersion` (литерал, сам не двигается) меняет payload снапшота → snapshot id → `RequestHash` ⇒ промах ВСЕХ чекпойнтов книги, то есть `--resnapshot` + пере-покупка. (б) тем же касанием — честность поверхностей: строка `VOICE` берёт `rules=` из конфига ВРЕМЕНИ ОТЧЁТА (`quality.go:436`), поэтому включение `gates.voice` после прогона печатает «измерено, чисто» для неизмеренной книги (воспроизведено); и она печатается при выключенном гейте, если `SpoilerLeaks>0` (дизъюнкция — поведение верное, формулировка отчёта/коммента неверна); знаменатель `BANKNOTE` = `len(states)` включая призрачные строки `retrieval_state`, тогда как соседний агрегатор их фильтрует (`quality.go:259-260`) ⇒ после пере-чанковки покрытие занижается
### Б-106 — тело строки 106 на 09.08.2026
**Слой 3 НЕ достроен: контракт редактора = full-regen, а целевой — узкие мандаты + ДИФФЫ.** `09-target-architecture.md:121-126` дословно: «research/07 предписывает узкие мандаты + диффы, а не полную перегенерацию — сейчас ровно наоборот (4 мандата, full-regen), что и породило хвост инверсий»; целевой контракт — редактор правит anchored search/replace-спанами с детерминированным Go-apply (ограниченный blast-radius, дешевле, аудируемо, структурно omission-safe). Пак-16 построил петлю РЕМОНТА (починка флагнутого спана постфактум), а не diff-контракт роли ⇒ это разные вещи. **Строки не было ни одной** — дыра учёта, найденная сверкой 02.08 (грепы `diff`/`мандат`/`span` по бэклогу = 0). Смежное: арм Q4b «diff-fidelity+style редактор» (`exp15:108`) не прогонялся НИ РАЗУ, хотя трек B объявлен завершённым. **ЭМПИРИКА ПОЛУЧЕНА (D39.108, экспы 19/20):** дифф-контракт механически исправен (комплаенс 0.966 на боевой единице, аппликатор 15/15), но по замороженному гейту Q4b диффы ОТЛОЖЕНЫ (fidelity-trap 11/12; дороже full-regen на dspro×ru ×2.52.9 при невскрытой причине; D21.4/D21.10 закрыты явно). **Эксп-21 (D39.117):** полный regen выигрывает у точечного ремонта (`C2/A_law` 3.75 Holm 0.0006) — довод против диффов, НЕ опровержение: чинил дешёвый фиксер по предложению, а не сильный редактор узким мандатом; гейт «перепроверить до стройки» в силе, приоритет понижен
### Б-108 — тело строки 108 на 09.08.2026
**Реестр ЗАГЕЙЧЕННЫХ ТРИГГЕРОВ + норма закрытия «по нужности».** ⚠ Первый жилец реестра, найденный тем же лендингом: **сиблинг-угроза экрана целевого языка** — выход на языке, БЛИЗКОМ к целевому (украинский/болгарский для ru): ни одного события не наблюдалось, но если появится, дискриминатор — плотность РУССКО-ЭКСКЛЮЗИВНЫХ букв (ы/э/ё) НИЖЕ ожидания, а НЕ замыкание алфавита (`EFFORT_HANDLE §5.4` шаг 8). Шесть остальных реопенов живут только в телах D-нот и не имеют места, где их поднимут: **МАСШТАБ** (uniformity-пасс D12 · эмбеддинг A7/D2 «реопен = измеренный recall-разрыв, только batch-time лейн» · STITCH «бюджет ИЛИ измеренный эффект размера инъекции» · канон-пин серий) · **вторая пара живьём** (C-триггер softsign-decl · автономная Decl-эмиссия) · **вторая книга того же жанра** (жанровый shared-слой) · **первая traditional-книга** (probe-литералы 时辰/千万/数十万 цельным дата-батчем) · **появление Go-кода платформы** (`go.work` — ⚠ `platform/go.mod` УЖЕ заведён, триггер наступил) · **перед стройкой fix-8** (ёлочный экстрактор атрибуции + корпус-замер). **+Два жильца приёмки D39.91:** Д5 — enum эффорта провайдер-слеп (`medium` на DeepSeek не определён, `max`/`xhigh` недостижимы; пер-модельную таблицу решено НЕ строить — механизм без носителя качества/денег, misconfig ограничен бюджетами и назван в доккомменте `ValidReasoningEffort`; триггер: первый живой инцидент кривого поведения уровня на проводе) · Д9 — аддитивные провайдеры отказываются бэнк/repair-гейтам, блок не несёт `reasoning_max_tokens` (fail-closed в лоадере; триггер: первая потребность гейта в аддитивной модели). **+Вахта D39.92:** вендор анонсировал смену МАППИНГА эффорта `deepseek-v4-pro` «early August 2026» ⇒ квирк 3а («low→high, эффорт не настраивается») может устареть (триггер: аномалия редакторского арма ИЛИ дата > 05.08 ⇒ пере-проба маппинга + сверка changelog; абсорбция в `00-provider-quirks.md` — полигоном). **→ СРАБОТАЛА: changelog вендора 13.08 — у V4-Pro и V4-Flash теперь уровни low/high/max (квирк 3а устарел, `max` стал достижим — двигает и Д5-жильца выше); тем же днём пик/офф-пик цены с 16.08 16:00 UTC (строка 172); changelog 31.07 официально подтверждает re-post-train флэша под слагом (провенанс D39.61); пере-проба поведением + абсорбция — полигоном по слову владельца (снято оркестратором 14.08).** Норма D39.66 «каждое отложено обязано ТЕМ ЖЕ лендингом получить строку или явное закрытие» нарушена ≥6 раз ПОСЛЕ собственной ратификации, и класс потерь узкий — теряются именно ЗАГЕЙЧЕННЫЕ РЕОПЕНЫ. Лечение: короткий реестр триггер→что вернётся→источник, сверяемый на каждом переходе горизонта (горизонт расписан до Ф3, точки сверки известны заранее) · **(D39.96)** тулчейн go1.27 переключит x/text на таблицы Unicode 17.0.0 МОЛЧА (`xTextVersion` пинит go.mod-строку, не таблицы) — триггер: любое движение тулчейна к 1.27 ⇒ строка 119 ПЕРВОЙ · **(D39.96)** CI-воркфлоу Forgejo (удалёнка 10.0.3, раннер не подтверждён; решение владельца 03.08 — не писать) — триггер: появление раннера ⇒ файл workflow; до того точка входа `make battery`. **+Два жильца аудита бэклога D39.140 (16.08):** граница пробы провода строки-однодневки 117 — CONNECT-туннель, CONTINUATION-фреймы и поведение под нагрузкой НЕ проверены, SETTINGS-фингерпринт не абсорбирован в `00-provider-quirks.md` (`archive/reports/ARCHGUARD_DOFIX_2026-08-03.md:173`; триггер: смена транспорта/HTTP-стека ИЛИ провайдер-аномалия класса «зависший стрим») · компонент «перспектива» нарративной гендер-аннотации пака-19 (строка 15 ушла лендингом D39.56 без этого компонента; смежный морфо-гейт рода — строка 82 — ДРУГОЙ предмет; триггер: подписание осей голоса при доборе идеала, строки 24/13б) · эскалационный кап НЕ прицениваает хоп — перелёт ≤1 хопа `escalate_to`-модели, оставлен и запинен D39.131 п.2(д) (триггер реопена: первый живой инцидент перелёта)
### Б-109 — тело строки 109 на 09.08.2026
**Продуктовые требования БЕЗ строки-носителя (сверка 02.08):** **ПТ-6** структурные традиции целевого языка (короткие zh-абзацы → читаемые ru-структуры) — держится только редакторскими мандатами в пар-промптах, поверхности измерения нет, в составе Ф2-гейтов (52) структуры абзацев нет · **ПТ-7** «слабый оригинал: подправлять художественно или быть верным» — вопрос владельца, не заведён даже в блоке «висит на владельце»; после ответа: режим параметром запуска (данные, не ветка Go) + мандат в промпте + гейт «улучшение vs отсебятина» · **ПТ-22** продуктовый ИНТЕЙК настроек (сегодня = рукописный `book.yaml`; частично покрыт 67 в части «версия исходника») · **ПТ-10** сам механизм веб-фетча (tool-calling, провенанс/кэш добытого, инжект под правилом «книга = недоверенные данные») — есть только гейт безопасности 70. Гигиена реестра тем же касанием: ПТ-24 не проставлена 101, ПТ-1/ПТ-11 не проставлена 105, ПТ-2 не проставлены 46 и 34а
### Б-121 — тело строки 121 на 09.08.2026
**archguard v2 (посадки приёмки D39.96 + §7-12/13 + §7-9). ⚠ Пункты (0а)/(0б) — РЕГРЕССИЯ против снесённого байт-гарда; дофикс ИСПОЛНЕН и принят D39.97 — (0а)/(0б)/(а) ЗАКРЫТЫ, плюс блокер type-param, ось имени и ложное срабатывание сверх промта; остаток строки — (б)/(в)/(г)/(д).** (0а) egressseam пропускает пакетную `llm.Complete(...)`: nil-`Selections` → молчаливый выход (`archguard.go:240-242`; фикс — fallback на `Uses`); (0б) пропускает интерфейс с llm-типами внутри композита (`Complete(ctx, []llm.LLMRequest)…``touchesPkg` не рекурсивен, `:79-82`); старый регекс `.Complete(` ловил обе формы; комменты «cannot be hidden» (`:250-253` и фикстура) фальсифицированы — переписать честно; (а) `runRequestSeam` обходит ТОЛЬКО `*ast.CompositeLit` (`archguard.go:197`): `var r pipeline.Request`+присваивание и `new(pipeline.Request)` проходят МОЛЧА — подтверждено на реконструкции: дыра ОБЩАЯ со старым гардом (не регрессия); фикс ~10 строк: симметрия ValueSpec/CallExpr со stage-сеамом + негативная фикстура, документирующая осознанные пропуски; (б) класс «встраивание · make+присваивание · дженерик-ноль» закрывается не свитчем, а ТИПОМ (нулевое значение `config.Stage` непригодно вне пакета); (в) политика build-тегов vet (сегодня default+live; чужой тег невидим — других в репо нет); (г) кандидат 4-й анализатор границы импортов — `backend/README.md:3` заявляет «проверяет `go list -deps`», что не автоматизировано ничем; (д) семь осознанных пропусков после дофикса 03.08 (принят D39.97; фикстуры БЕЗ want — покраснеют, когда начнут ловиться): llm.Ask-класс · пара внутри named-обёртки · встраивание/make/generic-zero · `internal/llm` исключён целиком (второй провод внутри адаптеров ловится только ревью) · внешние тест-пакеты · рефлексия (статикой не закрывается) · файлы за третьим сборочным тегом — всё, что закрываемо, закрывается типовым швом (б), не свитчем
### Б-122 — тело строки 122 на 09.08.2026
**Гигиена пака стандартов (не-блокеры приёмки).** `TestCheckerLabelsCandidates` — ни одного ассерта (дизайн-REPL в одежде теста) · сообщение `TestMemoryVersionPinsTheUnverifiedRenderRevision` не различает «изменился рендер»/«изменилась нормализация» · Makefile: regex GO_MIN_VERSION пропускает go1.26.01.26.3 (патч-уровень не проверяется) · включить `unconvert` (1) и `wastedassign` (2) — дешевле уже включённых по собственному правилу конфига · `.skips.log` не в gitignore (прерванная battery оставляет мусор в дереве) · Fprint-исключение errcheck скоупить на `cmd/` (сейчас весь модуль — будущий писатель файла экспорта получит молча съеденную ошибку) · `w\.Write`-исключение ключуется ИМЕНЕМ receiver-а вопреки собственному принципу конфига · miningstop-исключение глушит текстом весь файл — якорить на функцию · provenance-числа конфига дрейфуют от дерева (293/31/93 против 294/33/91; контрпример «все прод-Close — читатели»: `chunktest/epub.go:51`) · коммент c1 «echo mine unarmed» поправлен дофиксом 03.08 (снят) · инвариант-коммент `httpllm.go:77` «zero wire bytes» — верен для полей пинга, неверен для бампа x/net, который их привёз (находка-4 D39.96) — переписать честно
### Б-126 — тело строки 126 на 09.08.2026
**Потолок прогона — УПРАВЛЯЕМЫЙ, решение владельца 07.08: настройка в интерфейсе фронта, шкала от минимума (ноль выбрать НЕЛЬЗЯ, подсвечен серым) до максимума баланса аккаунта.** Форма снимает конфликт с Р7 бесплатно: максимум = баланс ⇒ движок всегда получает положительное число и состояния «выключено» не существует, валидатор `book.go:250` не правится. **Три следствия, ратифицированы формой (D39.110):** (а) **единица — НЕ доллары**, а главы (или % баланса): тогда минимум объясняет себя сам («одна глава»), а D39.84 цел — читатель не видит структуру расходов; доллары на пользовательском экране = осознанный амендмент D39.84, не побочный эффект виджета; (б) это потолок ПРОГОНА ⇒ в `book.yaml` он не пишется, движку нужен приём потолка на прогон — строка 145; (в) **максимум шкалы = ДОСТУПНЫЙ ОСТАТОК, то есть `Balance` как есть** — ⚠ испр. 08.08 (D39.115): формулировка «баланс минус открытые холды» была ОШИБКОЙ приёмки, вычитанием дважды. Холд — это дебет в момент взятия (`credits.go:179`), поэтому баланс уже без него; замерено: $10 грант + $1 холд = `Balance` 9, `Reserved` 1, «минус Reserved» дало бы 8. Спека несёт верную форму: `max_chapters` приходит УЖЕ подрезанным и по остатку, и по непереведённому хвосту книги, клиенту подрезать второй раз ЗАПРЕЩЕНО. **НОСИТЕЛЬ ПЕРЕСЧЁТА НАЗВАН (добор после верификации 07.08):** «сколько глав влезает в доступный остаток» считает ПЛАТФОРМА (по оценке движка) и отдаёт фронту ГРАНИЦЫ шкалы и выбранное значение в главах; сама формула наружу не выходит. Ручки под это в контракте НЕТ — сегодняшний `GET /v0/usage` несёт только процент, поэтому поверхность объявляется при правке спеки вместе с созданием прогона (работа П-1 + S3), и до её объявления фронт максимум шкалы взять неоткуда. Прежнее тело строки (механизм поднятия потолка для resume после ceiling-стопа: движок продолжает «once the ceiling is raised», `stagerun.go:488-489`) — половина этой же работы. Продуктовое слово стопа — К-8 у владельца