# backend/ — Go-бэкенд TextMachine Зона сессии «Бэкенд». Контракт — `docs/architecture/05-decisions-log.md` (свежая голова — с хвоста файла); целевая архитектура (7 слоёв, инвариант общности §0.1 — любая книга/пара/структура; статус стройки — шапка-таблица) — `docs/architecture/09-target-architecture.md`; норматив общности — `12-go-style-notes.md`; контракты Фазы 0 — `03-implementation-notes.md` (через баннер). **Язык-данные майнера — вне пайплайна: `internal/lang`+`configs/langpacks/` (D39.16); пар-калибровки — `configs/pairs/<пара>.yaml`, промпты — `prompts/<пара>/<роль>.md` по конвенции (D39.23).** **Подсистемы (`chunk`/`miner`/`checks`/`membank`) не импортируют друг друга и не импортируют `pipeline` — драйвер является композит-корнем (D39.23; проверяется `go list -deps`).** **`.env` не читать.** *(Актуализировано оркестратором 01.08 по мандату владельца, факты сверены по коду.)* ## Карта пакетов | Пакет | Что делает | Ключевые файлы | |---|---|---| | `cmd/tmctl` | CLI: `translate` / `status` (read-only N/M+паспорта глав+деньги, `--json`; работает ПРИ живом прогоне — store без flock) / `report` (request-log+леджер книги **+ per-run quality-report**: структурный KPI предл./нарратив-абзац, тире, cosmetic-strip/echo-rates, глоссарий-промахи, trust-gated — наблюдаемость, НЕ гейт; D39.2-T4. ⛔ **`BOOK CONSISTENCY`** — приоритет №1 по ОТГРУЖЕННОМУ тексту КНИГИ ЦЕЛИКОМ (ряд 406): на терм×главу два счёта — сколько раз ключ выстрелил в исходнике и сколько раз принятая форма вышла в текст, — откуда I1 «одна форма на термин» (главы, которые разошлись, названы) и I2 «отдана форма банка» (`D39.104`). Печатает ЗНАМЕНАТЕЛИ: судимо · без `dst` · **ключ не выстрелил в исходнике** · закрыто окном спойлера, — потому что «0 расхождений» и «не смотрел» иначе один и тот же ноль. Второй столбец — те же числа по равенству САМОЙ пост-проверки: разрыв между столбцами есть цена матчера, а не дрейф редактора. **`WAVE INJECTION`** — что показали ЧЕРНОВОЙ и РЕДАКТОРСКОЙ волне по отдельности (ряд 417, таблица `wave_selection`); у редакторской волны вытеснения бюджетом до этого не записывались нигде) / `export` (**экспорт-поверхность для полигона**: final_hash→checkpoint→checks.ExportNormalize, manifest-join c pending/ghost-учётом, ConfigDrift-поле, `--plaintext`; D39.5) / `build` (**писатель книги**: EPUB 3 + чистый txt из той же экспорт-проекции в `.book.` рядом с БД, пути — в `artifacts.book_files`; книга с ЛЮБОЙ дырой — pending/withheld/incomplete/ghost — по умолчанию отказ **exit 16** с перечнем, `--partial` пишет с пометкой на первой странице и маркером на каждой дыре; слова читателя — данные `configs/langpacks/<цель>/reader.txt`, вне снапшота; `--format epub,txt`, `--out`) / `redrive` (переатака флагнутых: `--chapter/--chunk/--reason/--dry-run`, D15.3) / `manifest` (**$0-производитель персиста манифеста глав/чанков**: нужен ДО первого прогона — дерево глав разобранной, но не запущенной книги) / `migrate` (**$0-write-open без прогона** (D39.134): read-only команды схему НЕ мигрируют. Грузит ТОЛЬКО `book.yaml` — ни цен, ни ключей на $0-пути (строка 146); restore point берёт ПОД ЛОКОМ и только когда шаг реально применяется (имя `<метка>-pre-migrate.db` — вне секундного неймспейса платного пути, строка 173); несовпадение схемы у read-only путей — типизированный отказ **exit 13** с машинным токеном `schema_mismatch found=N expected=M`) / `bank-apply` (**входная дверь шва**, D39.156/D39.158: приём документа решений владельца по банку, $0; отчёт v2 на stdout, отказ по существу — **exit 14**, незавершённая запись — **exit 15**) / `backup` (`VACUUM INTO` + `integrity_check`, он же pre-flight боевого прогона) / `seed-lint` ($0-проверка сида и дельты владельца). **ДВА потолка прогона, и они ортогональны.** `--ceiling-usd` — ДЕНЬГИ: перекрывает книжный `ceilings.book_usd` ТОЛЬКО на этот прогон, в book.yaml не пишется, ноль/отрицательное = отказ запуска; каппит КУМУЛЯТИВНУЮ трату книги, а не приращение прогона. `--max-units` (D39.165 §1б; механика и денежный отчёт — D39.170) — ОБЪЁМ: не больше N ВЫХОДНЫХ ЮНИТОВ (гранулярность `units_total` манифеста, та самая, в которой платформа продаёт главы) возьмут слот в этом прогоне — новые юниты, которые он начинает оплачивать, и уже отгруженные, которые он пере-делывает под сдвинутым снапшотом. $0-юниты (резюм, ре-пин) потолок не тратят; юнит, который прежний прогон НАЧАЛ и не отгрузил (обрыв между волнами или внутри волны), дописывается ВНЕ гранта, чтобы один юнит не брал слот дважды (строка 232) — но не больше N таких за прогон, в порядке книги, а остальные ждут как любой недоставленный юнит. Принимает только `translate`. **Остановка по объёму — ЗАВЕРШЕНИЕ (exit 0), а не пауза:** различение живёт в отчёте прогона и в логе. ⚠ На книге, которая МАЙНИТ банк, вторая покупка требует `--resnapshot` (авто-банк растёт между покупками и двигает edit-снапшот) — прогон предупреждает об этом в логе. main — тонкая обвязка: разбор/exit-коды/.env/рендеры в тестируемых функциях (полный перечень глаголов — `dispatchCommands` в `cmd/tmctl/main.go`) | `main.go`, `invocation.go`, `render.go`, `dotenv.go`, `bankapply.go`, `backup.go` | | `internal/llm` | OpenAI-совместимый транспорт + retry/backoff, **capability-слой** (budget_field/temperature/reasoning per-модель), провайдеры openai/local (no-proxy)/anthropic (DEPRECATED-референс); маршрутизация лейблами живёт в config/pipeline (канал B, один хоп `chain[0]`, fail-closed; D4/D39.26) | `httpllm.go`, `capability.go`, `provider_*.go` | | `internal/ledger` | Цены по usage (вкл. reasoning/cache-поля), `PriceForResponse` по фактической модели | `pricing.go` | | `internal/store` | SQLite (modernc, CGO-free), цепочка миграций `schema_version`, reserve/settle+checkpoint, chunk_status, глоссарий (+подписной цикл терминолога), ruby, retrieval_state, **`wave_selection`** (что показали каждой волне по отдельности — у `retrieval_state` ключ один на чанк, и следы двух волн складывались в одну строку), request_log (+`reasoning_in_completion` — НЕ NULL-по-умолчанию колонка: сколько уже оплаченного вывода было размышлением; `NULL` = провайдер не сообщил, и это НЕ ноль); `OpenReadOnly` — без flock/миграций/recovery для status/report/export (схема не совпала — типизированный `*store.SchemaMismatchError{Found,Expected}`, обе стороны); `Migrate` (+шов `beforeApply` под локом), `SchemaHead` — та самая `Expected`; write-open БД новее бинаря — ОТКАЗ (D39.134) | `ledger.go`, `migrate.go`, `glossary.go`, `store.go` | | `internal/config` | fail-fast загрузка models/pipeline/book; **эхо-мина-гейт** `echoMineViolation`; `CheckRunnable` блокирует неисполнимое (C2/fanout/judge); **пар-слой** (D39.23): `configs/pairs/<пара>.yaml` — калибровка нарезки + коридор длины пары; **промпт-резолв КОНВЕНЦИЕЙ** `prompts/<пара>/<роль>.md` из `Book.LangPair()`+роли, **fail-loud на пару без пакета** (ja-книга не едет молча через zh-промпт), осознанное исключение — `prompt_override` на стадии | `models.go`, `pipeline.go`, `pair.go` | | `internal/text` | Нормализация и рун-примитивы, общие для банка/майнера/чекеров (script-agnostic слой, `lang` НЕ импортирует): `NormalizeSourceKey`/`NormalizeTargetForm` (версионированный артефакт `NormVersion()`, фолдится в снапшот), `NormalizeSource`, `TokenizeScript` (алфавит цели из данных, Mn-фолд U+0301; `TokenizeCyrillic` = ru-дефолт-обёртка), `DenseScript` — единый дом sizing-таксономии (D39.64) | `norm.go`, `runes.go`, `source.go` | | `internal/seed` | Схема сид-YAML книги (`File`/`Term`/`Decl`/`Alias`) — общий артефакт: майнер её ПИШЕТ (mined-дельта), банк ЧИТАЕТ (`LoadGlossarySeed`); тип общий, поэтому эмиссия не может разъехаться с загрузчиком | `seed.go` | | `internal/lang` | Языковые ДАННЫЕ и пак-механизм: эмбед-плоскость `data/*.txt` (хешируется в `EmbeddedVersion()` → снапшот, D39.64) · langpack `Load`/книго-overlay (аллоулист файлов, fail-loud) · **манифест-по-каналам** (`manifest.txt` источника объявляет `source-morphology`/`transliteration`; манифест-less пак = все каналы, байт-идентичен) · единый реестр письменностей `script.go` (`LangScripts`/`IsCJKScriptLang` из `lang-script.txt`) · реестр целей из `data/target-.txt` | `langpack.go`, `embedded.go`, `script.go` | | `internal/terminology` | Роль терминолога (пак-20): смердженный банк + KWIC-контексты → батчи консолидации dst той же дешёвой моделью; языковой экран `target_script`; durable-деньги подписного цикла | `terminology.go`, `script.go` | | `internal/chunk` | Ингест (txt/epub, кодировки, ruby) + чанкер: `Ingest`/`IngestEncoded` → `Document`, `SplitChunks` → `[]Chunk` по `SegBudget`; структура источника (главы, CJK-числительные, heading-правило) — ДАННЫЕ из `lang` | `ingest.go`, `chunker.go`, `chunktest/` | | `internal/membank` | Банк памяти v2, memmatch-v4: Aho-Corasick, спойлер-окна, disposition, **trust-gated longest-match** (draft-длиннее НЕ съедает вложенный approved; отказ = громкая телеметрия — терм-дрейф закрыт в КОДЕ, D39.2-T1), post-check, рендереры инъекции, загрузка/валидация сида (`SeedLint`) | `memory.go`, `memseed.go`, `mempostcheck.go` | | `internal/miner` | Детерминированный оффлайн-майнер банка (WS3, $0): `MineBank` над нормализованными чанками + контраст-корпус → `[]Term`, `DeltaYAML` — сид-дельта на подпись владельца; все пар-данные приходят `*lang.Pack` | `miner.go`, `miner_*.go` | | `internal/checks` | Детерминированные $0-вердикты над текстом чанка, **target-aware ПО ДАННЫМ (D39.64): `Checkers` компилируется из цель-данных, `TargetActive()`/`TargetScriptNonLatin()` гейтят всё — цель без данных инертна, Go-веток по паре нет**: санитайзер v7 (fold-first, CJK/Hangul-leak, CJK-глосс-whitelist) + `ExportNormalize` (**export-contract слой 6**, no-op без цель-данных), cheap-гейты и DC-чекеры (пар-данные из пака), coverage-гейт эксцизии, regression-guard. Флаг-вокабуляр НЕ импортирует: возвращает факт, диспозицию назначает драйвер | `sanitizer.go`, `cheapgates.go`, `checkers.go`, `coverage.go` | | `internal/pipeline` | Композит-корень (драйвер): сетап `runner.go` → снапшот `snapshot.go` → сид `seeding.go` → цикл книги `bookrun.go` → волны `waverun.go` → петля чанка `chunkrun.go` → стадия `stagerun.go` → эскалация с ре-гейтом `escalation.go` → resume `resume.go`; classify/disposition; **реестр `roleInjectionRenderers`** (новая роль = данные+рендерер, не правка switch); `export.go`/`quality.go` — read-only проекции; `status.go` — тоже проекция, НО в нём же живёт **единственный деструктивный путь движка**: `Runner.Redrive` (греп `ResetChunkStages`) удаляет флагнутые `chunk_status` и их чекпойнты перед пере-атакой, то есть «read-only» про этот файл неверно (общая derivation member-drop = `memberDrops`); **read-out файлы для платформы** (D39.85 — приватный SQLite движка ей закрыт): `manifest.go` (структура глав/чанков + стабильный id главы, строка 100), `bankexport.go` (весь банк тремя статусами, 125); вместе с картой подписи и авто-банком в `mining.go` (`signatureMapPath` · `autoBankPath`) — все ЧЕТЫРЕ пишутся через `artifact.go` (write-then-rename — их читают ПОКА идёт прогон); человеческая стоп-таблица `.bank-stop.txt` — обычным `os.WriteFile`, БЕЗ этой гарантии, а машинной `.bank-stop.json` больше нет (снесена D39.158) | `bookrun.go`, `waverun.go`, `stagerun.go`, `export.go`, `manifest.go`, `disposition.go` | | `internal/bookfile` | Писатель книги: EPUB 3 (контейнер/OPF/навигация) и чистый txt из экспорт-проекции; формат знает только он, `tmctl build` даёт ему данные | `epub.go`, `txt.go`, `container.go`, `model.go` | | `internal/runevents` | ДВИЖКОВАЯ сторона шва кадров прогона (строка 103): конверт и payload'ы NDJSON-потока `events.jsonl` + append-only журнал; словарь исходов `finished`. Форма — здесь, КОГДА событие случилось — у драйвера, долговечная нумерация — у `store.events_outbox` | `runevents.go`, `journal.go` | | `internal/standdata` | Резолв путей корпусных данных ОТ КОРНЯ РЕПОЗИТОРИЯ по маркеру (`<репозиторий>/books`, D39.157 п.2); `$HOME` не читается вовсе, env-переопределения `TM_*` старше дефолтов | `standdata.go` | | `internal/obs` | trace_id, структурные логи (contextHandler несёт book/chapter/chunk/stage/role из ctx), safego | | ## Инварианты — ЛОМАТЬ НЕЛЬЗЯ (каждый закреплён тестами) 1. **Деньги:** reserve → call → **settle+checkpoint одной транзакцией**; `committed == SUM(checkpoints)` переживает kill -9 (`kill9_test.go`); цена — по фактически ответившей модели; потолки книга/день считают committed+reserved. **Одно исключение — явный `tmctl redrive`** (D15.3): после него `committed ≥ SUM(checkpoints)` — безопасное направление. 2. **Snapshot-дисциплина:** всё, что влияет на wire-байты ИЛИ вердикты (capability, память content-hash + `memoryMatchVersion`, context-assembly, версии classify/coverage/chunker/maxtok/**sanitizer (только при enabled)**, **эмбед-данные `lang.EmbeddedVersion()` (D39.64)**, эскалационные оверрайды, **биндинг пара→промпт** через BriefHash+PromptSHA256), свёрнуто в `snapshotID`; правка = громкий `--resnapshot` = переоплата книги (D15). Новый фолд обязан быть УСЛОВНЫМ по присутствию данных (правило §6.2, D39.60). 3. **Эхо-мина DeepSeek:** thinking НИКОГДА не отключать — `reasoning:"off"` там осознанный no-op; `echoes_when_thinking_off` + рекурсивный скан extra_body валят конфиг fail-fast. У grok off = ЯВНЫЙ `reasoning_effort:"none"`. 4. **Порядок classify:** refusal-blacklist / эхо источника (`sourceScriptShare` по ОБЪЯВЛЕННОМУ письму, D39.64) — ДО length/empty; retry-flagged только {length, empty} на той же модели; loop-чек ПЕРЕД удвоением max_tokens. Санитайзер — ТОЛЬКО на финальной стадии (isFinal) и только при активной цели (`TargetActive`). 5. **Эскалация:** ровно 1 хоп, результат РЕ-гейтится, retry-бюджет не сбрасывается, editor pinned, канал adult — только permissive (fail-closed). 6. **Детерминизм:** рендер — чистая функция snapshot; сортировки перед записью; никакого map-order в выводе; length-prefixed хэши; сорс-чанкер lossless (fuzz). 7. **Нейтральность адаптера:** `internal/llm` не знает про перевод — контент-вердикты в раннере (disposition-слой, post-settle). 8. **Экспорт-контракт (D39 слой 6, D39.5):** `checks.ExportNormalize` — эфемерная проекция (НЕ входит в request_hash/чекпоинты/деньги), применяется к КАЖДОМУ финальному тексту на всех выдающих поверхностях; **любая внешняя экстракция (полигон!) обязана читать `tmctl export`**, не сырые чекпоинты store — иначе видит ненормализованные байты и молча-неполную книгу. ## Подготовка ja-книги (чек-лист сида) - **Kana-написания имён = matchable.** Ruby-чтения захватываются на инжесте и авто-подцепляются как alias ручной записи (D16.4, `membank.AttachRubyAliasesToManual`). Имя, встречающееся ТОЛЬКО каной, внеси в `aliases:` руками — иначе «тихо пусто». - **Двойные чтения (強敵→とも) — под флагом:** длинное двойное чтение может дать CONFIRMED-ложь — при подозрении убери из сида. Полная дизамбигуация — Ф2 (B6). - **Kana-precision ≥4 не замерена** (minKeyLenPhonetic=3): кана-ключи не проверяются на границу слова (нет сегментации) — задача полигона + токенизатор B6. - ⚠ **ja-книга требует каталога `prompts/ja-ru/` (по файлу на роль)** — промпт-резолв конвенцией fail-loud'ится без него, называя ожидаемый путь (пример: закомментированный ja-блок в `example/book.yaml`). Контент пакета = данные под реальную книгу (инвариант общности §0.1), не сочинять. ## Как гонять Стенд: WSL2 (localhost из-под прокси = 403 — для local-вызовов no-proxy транспорт). Книга-стенд: `<репозиторий>/books/gu-zhenren/` (адрес по D39.157 п.2; GB18030; тексты и производные версионируются ОТДЕЛЬНЫМ git-репозиторием). Батарея — ОДНА команда, `make` из `backend/` (Makefile): ```bash make battery # build · vet (оба тег-набора, + архитектурные анализаторы) · gofmt · lint · test -race # в конце НАЗЫВАЕТ пропущенные тесты — вклеивать в отчёт, не опускать make battery-stand # то же + корпусные тесты (TM_MINER_PARITY=1 TM_CHECKER_LABELS=1): отсутствие данных ПАДАЕТ make mutations # мутационный гейт: подмножество cmd/tmmutate/mutations.json, помеченное `battery: true`, # сажается на КОПИИ дерева (рабочее не правится никогда) и обязано покраснеть. Ненулевой # выход даёт ЛЮБОЙ исход, кроме записанного в каталоге: выживший мутант · не-зелёный до # посадки пакет · гнилой пакет, файл или переименованный `run`-фильтр · «ничего не # прогналось» · записанный выживший, который теперь ловится · провалившийся restore · # невалидный каталог · пустое battery-подмножество · неизвестный `-id` · и гниль якорей — # она проверяется статически по ВСЕМУ каталогу, поэтому приходит и из записи вне # подмножества. Полный перечень — ветки, ведущие к ненулевому коду, в cmd/tmmutate/main.go # НЕ входит в battery намеренно: стоит примерно столько же времени, сколько сама # батарея, и идёт БЕЗ -race — то есть доказывает меньше, чем цель test, за ту же цену. # Точнее сказать нельзя, и это не небрежность: замеры на одной машине разъезжаются от # фоновой нагрузки шире, чем отличаются друг от друга обе цели, а состав подмножества # с тех пор ещё и вырос. # Состав подмножества живёт в каталоге (поле battery), не здесь make lint # golangci-lint 2.12.2 (версия пиновата в Makefile; конфиг — .golangci.yml) ``` Линтер ставится отдельно (бинарь вне репо, `make` его НЕ доустанавливает — ссылку печатает `tools-check`). Архитектурные инварианты живут в `internal/archguard` и запускаются двумя путями: `go vet -vettool` из `make vet` и обычный `go test ./internal/archguard/` (он прогоняет их по всему дереву). ```bash go run ./cmd/tmctl translate --config example/book.yaml # реальные вызовы — ключи в .env (пример: zh→ru) go run ./cmd/tmctl status --config example/book.yaml --json # $0, N/M+паспорта+деньги, живой прогон ок go run ./cmd/tmctl report --config example/book.yaml # $0, quality-report (KPI/rates) go run ./cmd/tmctl export --config example/book.yaml # $0, экспорт-JSON для полигона (--plaintext для человека) go run ./cmd/tmctl manifest --config example/book.yaml # $0, пере-строить персист манифеста глав/чанков (--json — сам документ) go run ./cmd/tmctl migrate --config example/book.yaml # $0, довести схему проекта до головы бинаря (деплой-порядок — D39.134 §3) go run ./cmd/tmctl translate --config example/book.yaml --ceiling-usd 0.5 # ДЕНЕЖНЫЙ потолок ТОЛЬКО на этот прогон, book.yaml не пишется go run ./cmd/tmctl translate --config example/book.yaml --max-units 10 # ОБЪЁМНЫЙ потолок: не больше 10 выходных юнитов возьмут слот; стоп = завершение (exit 0) # live-conformance (реальные провайдеры, платно, вне CI): set -a; . ./.env; set +a; TM_LIVE=1 go test -tags live -run TestLive -v ./internal/pipeline/ ``` ⚠ **«Зелёное» ≠ «всё проверено»: четыре теста — ИЗМЕРЕНИЯ на корпусе, а не юнит-тесты, и на чистом клоне они молча скипаются.** Голая батарея выше их НЕ выполняет, поэтому «батарея зелёная» без списка SKIP — неполный отчёт, а не приёмка. | Что тёмное без данных | Флаг | Данные | |---|---|---| | `TestMinerFullBookParity` — единственная сверка Go-порта против Python-эталона на полной книге | `TM_MINER_PARITY=1` | jieba-словарь по внутрирепозиторному ПУТИ `eval/exp16/data/`, но ВНЕ git (`eval/.gitignore:4`) — клон его не получает, регенерируется из jieba 0.42.1 (SHA в `experiments/16-bank-mining.md:183`); плюс `<репозиторий>/books/gu-zhenren/{rerun/records.json, guzhenren-seed-v2.yaml}` | | `TestCheckerLabelsBaseline`, `TestCheckerLabelsCandidates`, `TestK6LabelsBaseline` — единственный замер precision/recall $0-гейтов | `TM_CHECKER_LABELS=1` | `<репозиторий>/books/gu-zhenren/labels/` | Пути дефолтятся от КОРНЯ РЕПОЗИТОРИЯ, найденного по маркеру (`internal/standdata`; `$HOME` не читается вовсе — D39.157 п.2), так что клон под любым пользователем и по любому пути находит то, что есть; точечные переопределения — `TM_CHECKER_LABELS_DIR`, `TM_MINER_PARITY_{CONTRAST,RECORDS,SEED}`. **Golden-гард детерминизма** (`golden_test.go` + `testdata/golden/`): пинит бит-в-бит snapshotID, request_hash, wire-тела, вердикты и resume-байты. Обновлять ТОЛЬКО на ратифицированной смене поведения: `TM_UPDATE_GOLDEN=1 go test ./internal/pipeline/ -run TestGolden`; при re-capture — **маскированный структурный дифф** (хеши/версии → плейсхолдер) обязан быть пустым, если смена версий не меняла вердиктов. ## Известный техдолг (не трогать молча; ЕДИНЫЙ трекер — таблица «Бэклог» в `docs/PROGRESS.md`, номера строк = она) - **D15.2:** READ-половина `tmctl export` построена (D39.5); annotation/override-половина и content-addressed resume отложены (строка 49). F3 at-most-once — после D15.2 (строка 50). - **`Retries` НЕ фолдится в снапшот** (pre-existing, D39.4-LOW; сознательно так же для ручек `RegenerateEchoBeforeEscalate`, D39.64, и `LowerEffortOnEmpty` — дефолт «выключено» = прежнее поведение): в крэш-окне пониженный ретрай-бюджет молча бросает оплаченные OK-чекпоинты. - ⛔ **ЛЕКАРСТВО ОТ ДВОЙНОЙ ОПЛАТЫ ПОСТРОЕНО И СТОИТ ВЫКЛЮЧЕННЫМ.** `retries.lower_effort_on_empty` отвечает на ПУСТОЙ ответ ступенью усилия ниже при том же бюджете вместо удвоения бюджета; дефолт `false`, то есть на боевых книгах пока едет прежнее поведение и прежняя трата (замер живого прогона 11.09: 4 вызова из 27 вернули ничего и стоили **$0.106472 = 25.4 %** цены книги). Носитель условия — ряд бэклога **433**. Условие: замер оси `off`/`low` на плотном CJK, ПАРНЫМ дизайном на повторах (одиночный розыгрыш DeepSeek ошибается на порядок, квирки §3д), **с эхо-контролем ИМЕННО в РЕДАКТОРСКОЙ роли** — ниже усилие пере-вооружает эхо-мину, а опубликованные числа эха сняты на роли ПЕРЕВОДЧИКА, и квирки прямо говорят, что редакторская клетка не мерена. ⚠ Адресата у замера сегодня нет: полигон выведен из скоупа (`D39.248`). Цена флипа названа у самого ключа: попытка 0 не двигается, снапшот не двигается, пере-покупается по одному вызову на единицу, у которой уже лежит сохранённая попытка ≥1 (замер по машине: таких единиц 58 на 24 книгах). - ⚠ ALTER-шаги миграций v8+ НЕ идемпотентны вопреки шапке `migrate.go` — полу-применённая БД не сходится (строка 49а). - Открытый остаток строки 79 — граница ПЛОТНОСТИ письма Go-константой `cjkScriptNames` (`internal/lang/script.go`). - `prompts/zh-ru/editor-mono.md` — DORMANT-референс D13.1 (не боевой, тестом закреплён); боевой = `prompts/zh-ru/editor.md` v3-discourse (БИЛИНГВ). Глосс-капы санитайзера (6 CJK / 24 лат-токен) — tunable-эвристики (D39.5). Флоры min_max_tokens = схема (D24.3). - Хвост LOW/NOTE-находок адверсариала D39.4 (14 LOW + 14 NOTE) — диспозиция **«не чинить»**, носитель D39.4/D39.5, текст — `docs/archive/reports/SMALLPACK_TAILS_2026-08-02.md` (греп `D39.4 ledger-очередь`); живые пересечения — строки 43/78. ⚠ Прежний указатель на строку 53 мёртв: она закрыта D39.82 (`putUniqueDC`).