@startuml pipeline title TextMachine — поток перевода книги (2026-08-08, синхронизировано с D1–D39.25, пост-пак-16: волновой исполнитель; + блокеры контракта 99/100/101/125/145)\nПометки: [OK] = построено · [FLAG] = построено за enabled:false · [Ф2] = планово skinparam wrapWidth 260 start :Открытие Runner [OK]: строгий конфиг-стек (book.yaml + configs/pairs/<пара> + pipeline-*.yaml + models.yaml, KnownFields-декод), langpack пары + книжный overlay langpack_extend, промпт-конвенция prompts/<пара>/<роль>.md (fail-loud); :Ingest [OK]: txt (главы по \\f) / epub по spine, GB18030-детект/конверсия, ruby-захват, NormalizeSource; :Сид банка ДО снапшота [OK]: glossary_seed + ruby-кандидаты + mined_delta (Source:mined — только в ENRICHED) − mined_rejects · материализация BASE- и ENRICHED-банка; :Чанкинг [OK]: draft-чанки по ВЫХОДНОМУ токен-бюджету (SegBudget пары: фертильность CJK/other), edit-единицы декаплены · precompute sticky по BASE-банку одним упорядоченным проходом; :МАНИФЕСТ глав и чанков [OK] (строка 100): сайдкар рядом с БД — project_db + .manifest.json, атомарная запись · СТАБИЛЬНЫЙ id главы = хеш ИНГЕСТИРОВАННОГО текста главы (переживает пере-чанковку и правку соседней главы) · id ЮНИТА, наоборот, умирает с разрезом: глава + тег разреза + leader-индекс · ключ годности = исходник+чанкер+сегментация+форма пайплайна+пак+embed+norm · пишет любой write-путь, до прогона — команда tmctl manifest; :ЭКСПОРТ БАНКА [OK] (строка 125): project_db + .bank.json — весь банк тремя статусами, id из ключа уникальности (не автоинкремент glossary.id) · пишется на границах стадий; note right Снапшот per-wave [OK]: draft-волна пинит BASE-банк, edit-волна — ENRICHED (обогащение двигает ТОЛЬКО edit — «переоплата ОДНА», D30.9/D39.12). Фолдятся: BriefHash, langpack Version(), промпт-SHA, capability, версии вердикт-осей. Правка данных между прогонами = громкий --resnapshot. Внутри снапшота: $0-резюм (rerun2 — доказан ×3 арма). end note partition "ВОЛНА ДРАФТА [OK] (∥ workers, по draft-чанкам)" { :Сборка контекста [OK]: кэш-префикс (system+brief) + src→dst-инъекция глоссария из precomputed selection (Aho-Corasick, trust-gate, спойлер-hard-reject, sticky); :runStage [OK]: чекпойнт-хит → $0-реклассификация · иначе Reserve (потолки книга/день · потолок ПРОГОНА --ceiling-usd перекрывает книжный book_usd, строка 145, в хеши не входит) → вызов → Settle+чекпойнт ОДНОЙ tx → classify ПОСЛЕ денег; :Банкнота ⟦TM-BANK-v1⟧ [FLAG]: срез с RAW-драфта до classify · чистый драфт → derived-чекпойнт; if (Вердикт чанка? [OK]) then (транзиент: length/empty) :same-model retry, удвоение max_tokens (до regenerate_before_escalate); elseif (контент-провал: echo/refusal/loop) then (детерминированный) :Refusal причино-агностичен (D12/D39.25): детектим ФАКТ отказа, не причину · single-hop эскалация escalate_to под budget_usd + РЕ-ГЕЙТ · иначе flag+skip+continue (D2); else (ok) endif } if (Майнинг вкл. И дельта непуста? [OK]) then (да) :МАЙНИНГ-СТОП: MineBank по нормализованному источнику против контраста (сид и reject-set исключены); :ТЕРМИНОЛОГ [OK]: Merge(WHICH∪WHAT) → KWIC → ранг §C2-3 (голос по ФОЛДУ целевой формы, сырые формы в показе) → батчи ЮНИТАМИ: серия (равнодлинная) + СЕМЬЯ (общий корень: имя-префикс либо реалия-суффикс, данные скрипта) и containment-пара — юнит целиком в ОДИН вызов, кап членов; :$0-экраны: канон-конфликты · КОНФЛИКТЫ СВОИХ ЖЕ консолидаций · label-mismatch → стоп-таблица (сигналы топ-варианта · invented · уверенность · конвенций); :Сайдкары стопа [OK]: .bank-stop.txt — человеку · .bank-stop.json — машине (строка 101): ПОЛНАЯ таблица, списки списками, «роль промолчала» = null, не 0 · плюс обновление экспорта банка; :signature map → прогон ОСТАНОВЛЕН (exit 3); :Владелец: каждый терм → promote в mined_delta (approved+dst) ИЛИ decline в mined_rejects; :Ре-ран: драфт $0-резюм · пустая дельта → авто-континью; else (нет / выкл) endif partition "ВОЛНА РЕДАКТУРЫ [OK] (∥ по edit-единицам, ENRICHED-банк)" { :Единица = конкатенация member-выходов драфта (редактор НИКОГДА не пере-рендерит драфт — инвариант) · флагнутый member ВЫБРАСЫВАЕТСЯ из редактуры (c-lite, D39.18); :СВЕЖИЙ memory.Select по источнику единицы над ENRICHED → CONFIRMED-констрейнты билингв-редактору (deepseek-v4-pro ИНТЕРИМ, D39.22 · glm-5 резерв); :runStage: тот же контур reserve→вызов→settle+чекпойнт, ретраи/эскалация/ре-гейт — как в драфте; :Гейты финала [OK]: intrinsic classify всегда · санитайзер D30.3 (косметика → strip + экспорт flagged) · глоссарный post-check (флаггер всегда, гейт opt-in) · cheap-гейты v4 → retrieval_state (observability, НЕ вердикт); :Repair-петля [FLAG] (пак-16, D39.24) — суб-шаг финальной стадии: checks.RepairCandidates (позиционная детекция, вердикт-нейтральная) → ≤N таргет-вызовов по span, промпт prompts/<пара>/repair/<класс>.md → 6 гардов + ПОЗИТИВНЫЙ ре-гейт + анти-удаление · ⟦TM-NOCHANGE⟧ = успех-декляйн · фикс → derived tm-repair-v1 · ВКЛЮЧЕНИЕ платного пути = решение владельца по замеру остатка; :Чекпойнт финала [OK]: в TM коммитится только прошедшее гейты · ApplyHeading на первую единицу главы; } :Read-модели $0 [OK]: tmctl status (wave-aware) · ПОФАЗНЫЙ прогресс draft N/M и edit N/M в юнитах (строка 99) — сквозной счётчик стоит на нуле всю черновую волну · quality-report (структурный KPI + residual-скан RepairCandidates = замер остатка для решения о repair) · очередь флагнутых → человек (redrive точечно); note right Источник манифеста для status и export по умолчанию — ПЕРСИСТ (строка 100), а не пере-нарезка: 1.4 с CPU → 0.05-0.12 с на книге 23 МБ. Протухший ключ = откат на полную нарезку, подать устаревший знаменатель нельзя по построению. Пере-нарезку сохраняют пути, которым нужен ТЕКСТ: export --pairs, контент-ветка ре-билла, quality-report. end note :Экспорт $0 [OK]: манифест + final-строки chunk_status, checks.ExportNormalize байт-в-байт с translate, --pairs (source-колонка), счётчики pending/ghost/ConfigDrift; note right Канал B (18+) [пак-17, в дизайне]: generic content-labels × provider-capabilities (D39.25) — в движке НЕТ понятия «18+». Сейчас интерим: Adult bool + channel:adult ТИП-изоляция + линт. Ф2: голос/состояние D21, native-Gemini судья, C2/C3. end note stop @enduml