textmachine/backend
2026-09-17 07:27:05 +03:00
..
cmd Land the bank-role retry ladder: the attempt loop moves into one contour, bank roles reach the verdict the engine already computed, and two notes ratify the round 2026-09-17 07:27:05 +03:00
configs Land the bank-role retry ladder: the attempt loop moves into one contour, bank roles reach the verdict the engine already computed, and two notes ratify the round 2026-09-17 07:27:05 +03:00
docs Land the bought-means-delivered pack: the stop mark a refused retry leaves, the reader and operator wording for it, the echo metric split by stage, and the catalogue that guards them 2026-09-16 16:45:40 +03:00
example Land the engine wire batch: gender is produced and reaches the editor, the target-language screen refuses a foreign completion on the live path, and the law block gives one order per source term 2026-09-04 17:03:04 +03:00
internal Land the bank-role retry ladder: the attempt loop moves into one contour, bank roles reach the verdict the engine already computed, and two notes ratify the round 2026-09-17 07:27:05 +03:00
prompts/zh-ru Land the engine wire batch: gender is produced and reaches the editor, the target-language screen refuses a foreign completion on the live path, and the law block gives one order per source term 2026-09-04 17:03:04 +03:00
.env.example Drop Anthropic from the model stack per owner decision: remove provider and Claude models, repoint escalation and C2 judge to non-Anthropic 2026-07-04 16:35:52 +03:00
.gitignore Take in the engine zone's follow-up: two arm headers now say what the diff says, and the battery stops leaving its harvest log in the tree 2026-09-08 00:51:46 +03:00
.golangci.yml Add pinned golangci-lint config with number-grounded exclusions and a Makefile battery target as the single entry point, documented in the backend README 2026-08-04 01:48:58 +03:00
go.mod Bump x/net to 0.57 and x/text to 0.40 with lockstep normalization pins and golden re-capture; only hash fields move, live probe green per row 117 2026-08-04 01:45:40 +03:00
go.sum Bump x/net to 0.57 and x/text to 0.40 with lockstep normalization pins and golden re-capture; only hash fields move, live probe green per row 117 2026-08-04 01:45:40 +03:00
Makefile Make the book's own consistency answerable from the text a reader holds: two invariants over shipped output, their denominators printed, and the wire between instrument and run pinned 2026-09-11 16:16:52 +03:00
README.md Record what a call's reasoning cost, name and count the generations that came back with nothing, and add the lower effort step behind a key that ships off 2026-09-11 21:19:55 +03:00

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 из той же экспорт-проекции в <project_db>.book.<fmt> рядом с БД, пути — в 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-<tgt>.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/IngestEncodedDocument, 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):

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/ (он прогоняет их по всему дереву).

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).