textmachine/docs/architecture/06-memory-risk-registry.md

41 KiB
Raw Blame History

Реестр рисков банка памяти (v1, 2026-07-04)

Выход сессии «Валидация банка памяти» (промт docs/MEMORY_RESEARCH_SESSION_PROMPT.md). Это вход для бэкенд-сессии перед фиксацией схемы store/глоссария Фазы 1 (шаг 4 «миграция памяти v2» держится до этого реестра — PROGRESS §Память, D7).

Метод: многоагентный ресёрч по 7 направлениям + независимая адверсариальная верификация каждой находки по первоисточникам + локальный эмпирический бенчмарк retrieval на реальном PD-корпусе (eval/retrieval_bench.py, eval/data/retrieval_bench/) + чтение реального кода backend/. Полный разбор с источниками и датами — docs/research/13-memory-bank-validation.md. Продуктовый код здесь не пишется — реестр описывает требования к нему.

Главный вывод для чтения таблицы. Опасение владельца («retrieval вернёт пусто/мусор → перевод деградирует тихо») уточнено литературой: пустой retrieval — нормальное и безопасное состояние детерминированного горячего пути (большинство чанков законно не содержат ключей глоссария); откат к базовому поведению модели. Реальная тихая деградация — это ошибочная инъекция (омограф/короткий алиас/спойлер-утечка/низкорелевантный эмбеддинг-сосед): подсунутая строка глоссария работает как мягкая инструкция, и сильная модель ей послушно следует — уверенность растёт, а точность падает (arXiv 2510.00829). Поэтому вся защита строится на двух принципах: «лучше ничего, чем мусор» (не добивать токен-бюджет соседями — arXiv 2401.14887, 2310.01558) и точность важнее полноты на горячем пути, плюс обязательный детерминированный post-check как главный (и почти бесплатный) детектор тихой деградации.

Легенда

  • Слой защиты: DET — детерминированный код (горячий путь или батч, без LLM); LLM — вызов модели (только батч/раз в главу, никогда не в горячем пути инъекции); HUMAN — ручное подтверждение/политика.
  • Вер./Тяж.: вероятность (В/С/Н) и тяжесть (🔴 критич. / 🟠 высокая / 🟡 средняя). 🔇 = тихая деградация (проходит без явного сигнала — приоритет владельца).
  • Фаза: по плану MVP (1 = «перевод книги целиком», 2 = Python-сайдкар/эмбеддинги/судьи).
  • Статус: закрыто дизайном v2 · 🔶 требует уточнения/новой работы в схеме или коде · дыра, действие обязательно до фиксации схемы.

A. Горячий путь: robustness инъекции памяти

# Сценарий отказа (как ломает перевод) Вер./Тяж. Механизм защиты Слой Где в коде/схеме Фаза Статус
A1 Пустой/слабый retrieval трактуется как ошибка → пайплайн «добивает» бюджет ближайшими эмбеддинг-соседями или фоновыми записями → near-miss дистрактор роняет качество (до 67%, 2401.14887; NLI-фильтр спасает, 2310.01558) С / 🟠🔇 «Лучше ничего, чем мусор»: на пустом ключевом матче инъектить только активные sticky-записи; бюджет допустимо недозаполнить; фильтр по точности перед сортировкой по приоритету (не только approved>auto) DET сборщик контекста (render.Messages, инъекция между system и user, render.go:133-136) 1 🔶 добавить precision-gate; в research/05 §5.2 бюджет заполняется по приоритету, не по точности
A2 Ошибочная инъекция (омограф/короткий алиас/низкорелевантный сосед) → модель послушно следует мягкой инструкции, уверенность растёт, точность падает (2510.00829) — главный источник тихой деградации С / 🔴🔇 Трёхстороннее явное решение на каждую запись-кандидат: CONFIRMED (точный ключ/лемма, approved, спойлер-валидна) → авторитетно; AMBIGUOUS (короткий/общий алиас, коллизия поверхности, auto/draft) → инъекция с явной пометкой «unverified — проверить» + принудительный post-check; REJECT → выкинуть и залогировать. Паттерн CRAG (accept/verify/reject), но каждая ветка наблюдаема DET сборщик контекста + disposition-запись 1 спроектировать трёхсторонний disposition; сейчас инъекции нет вовсе
A3 CJK-подстрока срабатывает на чужом смысле (阿Q, 送灶-обряд vs «送到灶», 炎=пламя vs имя 萧炎, 道/气/修 полисемия) → инъекция неверной записи В / 🟠🔇 Запрет одиночных Han/каны/латинской буквы как самостоятельного ключа (schema-констрейнт, не соглашение); longest-match; замена только целой сущности; условные judgment word (GalTransl post-dict). Эмпирика (retrieval_bench): точный многосимвольный подстрочный матч — precision 1.0, 0/4 ложных срабатываний на этих ловушках; char-BM25/фаззи — все 4 ловушки в top-5 → фаззи/одиночные ключи на горячий путь не пускать DET матчер ключей; glossary.min_key_len per-язык; поле sense 1 🔶 ключевая гигиена как констрейнт схемы (новое)
A4 Молчаливо-пустой матч из-за орфографии (упрощ./трад. 鲁/魯, полу/полноширинные, кандзи↔кана 鈴木/すずき, ruby-чтения) → ключ есть в тексте, но не в той форме → запись не инъектируется В / 🟠🔇 Обязательный слой нормализации перед матчем (NFKC + OpenCC упрощ./трад. + kana-folding + захват ruby-чтений), симметрично к ключам/алиасам и тексту чанка; версионированный тестируемый артефакт DET pre-match нормализация (связать с pre-replace GalTransl); поле aliases 1 в research/05 §5.2 нормализация матчинга не описана — самый вероятный баг «тихо пусто»
A5 Местоименные чанки (имя не встречается, только «он/她/彼») → нужная запись не активируется В / 🟡 Sticky scene-inertia (персонажи из предыдущих 12 чанков остаются); это состояние, а не retrieval — не решать эмбеддингами DET sticky-состояние сборщика 1 (в дизайне; см. World Info sticky)
A6 Lost-in-the-middle: глоссарий закопан между стопкой резюме книги/арки/главы Н / 🟡 Авторитетный блок глоссария — рядом с чанком (конец промпта), не в середине; при наших размерах инъекции эффект мал, но избежать бесплатно DET раскладка render.Messages (согласовать с кэш-раскладкой Р5) 1 🔶
A7 Эмбеддинг-порог как тихий гейт (Фаза 2): единый глобальный cosine-порог ведёт себя как эвристика entity-overlap и ломается на сдвиге распределения (репродукция CRAG, 2603.16169); zh/ja→ru литература — и есть сдвиг С / 🟠🔇 Эмбеддинг-слой держать low-trust: калибровать порог per-книга/язык на held-out чанках; использовать для генерации кандидатов на ревью (флешбеки, редкие термины), не для тихой инъекции в горячий путь DET+HUMAN Фаза-2 retrieval-слой 2 🔶 research/05 подаёт эмбеддинги как безобидный «второй эшелон» без порога/воздержания

B. Корректность глоссария (реестр «термин → перевод»)

# Сценарий отказа Вер./Тяж. Механизм защиты Слой Где в коде/схеме Фаза Статус
B1 Устаревшая запись (stale): первый (наименее информированный) перевод имени лочится на 1000 глав. DelTA Proper Noun Records = first-translation-wins без обновления и без детекции конфликтов → сам механизм ПОРОЖДАЕТ stale/contradiction/drift, а не защищает (2410.08143; LTCR-1 меряет самосогласованность, не корректность — консистентно-неверное имя даёт 100%) С / 🟠🔇 Append-only журнал ревизий рендеринга (src, old_dst, new_dst, editorial_ts) отдельной осью от спойлер-окна; смена approved-рендеринга → LLM/HUMAN-подтверждение → точечный ре-перевод по ключу (ReTranslation-by-keyword, LinguaGacha); content-hash в snapshot принуждает перевыполнение DET(журнал)+HUMAN схема: ось «редакционное время» ≠ since_ch/until_ch; ключ TM (Р6) 1 (поле) / 2 (ре-перевод) DelTA-PNR наивно нельзя; в research/05 §4.2 подан как позитив
B2 Противоречивые записи: один термин переведён двумя способами / два исходных → один перевод С / 🟠 UNIQUE(src, sense, window) на approved-строках + обратный GROUP BY dst (детект коллизии инъективности); LLM-адъюдикация конфликтов раз в главу батчем (паттерн write-time из Zep/Graphiti, но в SQLite без Neo4j); HUMAN решает DET(констрейнт)+LLM(батч) схема: индексы/констрейнты; тип ranked-set для рангов (v1.1) 1 (констрейнт) / 2 (адъюдикация) 🔶
B3 Разрешение алиасов (名/字/号/прозвища/титулы: «один персонаж = 36 людей») С / 🟠🔇 Alias-граф с типами (имя/цзы/хао/прозвище/титул) + name-tables (фамилия/имя раздельно); NER-гейт «новая форма имени-подобного токена = блок/флаг» DET+LLM(экстракция)+HUMAN схема: aliases→граф (D7 v1); NER-гейт 12 🔶 (04-unhappy §1)
B4 Дрейф «термин → утверждённый перевод» по ходу книги (approved-термин передан иначе) С / 🟠🔇 Post-check регэксп по склонённым формам (через decl/лемматизатор): approved src/алиас во входе → лемма dst в выходе? нет → флаг редактору; прономинализация не штрафуется, только approved (процедура Р7) DET гейт консистентности глоссария (Р7); поле decl 1 (Р7) — но см. E-post-check
B5 Западные имена через катакану/фонозапись (约翰→«Юэхань» вместо «Джон», ヴァイオレット, васэй-эйго マンション=квартира) С / 🟡 Поле translit_policy для псевдоевропейских имён; словарь васэй-эйго (v1.1) DET схема: translit_policy (D7 v1) 1 (поле) / 2 (словарь) (04-unhappy §9, D7)
B6 Нонконформность стандарту транслитерации (zh→ru — Палладий, ja→ru — Поливанов): имя может быть внутренне консистентным, но систематически неверным по стандарту — ось корректности, ортогональная самосогласованности С / 🟠🔇 Валидация dst имён собственных против таблиц Палладия/Поливанова на коммите термина; отклонение — флаг/политика проекта DET+HUMAN схема: правило на коммите dst; пресеты стандарта 2 🔶 эмпирически подтверждён (exp06): локаль на zh даёт Палладий-мусор (阿Q→«Ах-Чу», 赵太爷→«Тяо Тай-Я»), рендер zh 0.26; даже облако-топ лишь 0.75 (часть zh→ru реалий без единого канона) → детерминированная таблица Палладия/Поливанова обязательна, «сильной моделью» не закрывается

C. Спойлеры и род (темпоральная ось)

# Сценарий отказа Вер./Тяж. Механизм защиты Слой Где в коде/схеме Фаза Статус
C1 Спойлер-утечка: retrieval отдаёт факт, которого текущая глава знать не должна («учитель — предатель» из гл. 200 в гл. 5) С / 🟠🔇 since_ch/until_chжёсткий reject с логируемым событием на ОБОИХ путях (ключевой И эмбеддинг), не UX-фича; это safety-гейт DET фильтр окна в сборщике; поля since_ch/until_ch 1 🔶 research/05 трактует спойлер-окно как продуктовую фичу, не как safety
C2 Поздний гендер-твист / намеренная неоднозначность (данмэй, 女扮男装, ta) → вмороженные главы закоммитили род С / 🟠 gender=hidden(until_ch) + «дисциплина уклонения» в промпте (безличные/настоящее время) + детерминированный блокер утечки рода; ретро-патч-протокол «с гл. N — так» с показом стоимости DET+HUMAN поле gender=hidden; протокол релиза твиста (D5.1) 2 🔶 (04-unhappy §2, D7)
C3 Утечка рода через русский глагол прош. вр. («сказал» vs «сказала») — gender=hidden защищает существительные/местоимения, но глагол согласуется с полом без имени рядом С / 🟠🔇 Морфо-гейт post-check должен сканировать глагольные формы прош. времени, не только родовые существительные DET (Python-морфология) морфо-гейт Фазы 2 (pymorphy/Natasha) 2 под-специфицировано (находка критика; 04-unhappy §2 говорит о согласовании, но не выделяет глагол-без-имени)
C4 Склонение транслитерированных CJK-имён: какие имена склоняются в русском — правило-зависимо (многие ja на гласную несклоняемы, zh-моносиллабы варьируются) → decl для CJK-имён нетривиален → источник ложных флагов/форсов С / 🟡 Правила склоняемости per-пара при заполнении decl; при неуверенности — флаг, не форс DET+LLM(заполнение)+HUMAN заполнение decl (один дешёвый LLM-вызов на коммит термина, Р3) 12 🔶 (находка критика)

D. Иерархическая память (резюме) — «вторая половина банка», почти не покрыта

Строки D1/D2 ниже — ID секции D этого РЕЕСТРА (буква-секция + номер, как A1/E1/F1); НЕ путать с решениями D1D12 из 05-decisions-log.md.

# Сценарий отказа Вер./Тяж. Механизм защиты Слой Где в коде/схеме Фаза Статус
D1 Пропагация ошибки резюме: галлюцинированный/неверный факт в резюме главы инъектируется как авторитетный контекст и компаундится по 1000 глав — post-check-аналога у резюме НЕТ (у глоссария есть GalTransl layer-3, у резюме — ничего) С / 🟠🔇 Отдельный gate для резюме: сверка сущностей резюме против глоссария/coverage; батч-судья фактичности резюме раз в арку; резюме — не «авторитет по умолчанию» DET+LLM(батч) схема резюме; гейт резюме 2 первоклассный пробел — все направления ~на 90% про глоссарий (находка критика)
D2 Whole-book RAG по резюме — недоказанное расширение: DelTA LTM = ограниченное онлайн-окно ~20 предложений уже-переведённого префикса + LLM-ретривер per-предложение, не whole-document RAG и не детерминированный С / 🟡 Держать как гипотезу к проверке, не как доказанное; whole-book семантический слой — Фаза 2, low-trust (см. A7) DET+HUMAN Фаза-2 LTM 2 🔶 research/05 §4.2 гласит «retrieval по всему документу» — сверх того, что DelTA показал

E. Детерминизм и принуждение (Р3 трёхслойная защита)

# Сценарий отказа Вер./Тяж. Механизм защиты Слой Где в коде/схеме Фаза Статус
E1 Мягкий глоссарий молча не сработал: LLM оставляет 1736% терминов неиспользованными (2310.05824) — soft following выигрывает по качеству (DuTerm en-ru, 2511.07461; WMT23 COMET-QE), но проигрывает по adherence В / 🟠🔇 Post-check обязателен и это post-CHECK, а не слепой post-replace: для каждого approved-термина с src в чанке — проверить, что склонённая форма dst есть в выходе; нет → флаг/точечный ре-перевод. Это главный (и почти бесплатный) детектор тихой деградации: ловит и «модель проигнорировала», и «мы инъектили неверное» DET post-check гейт (Р7); поле decl 1 🔶 повысить приоритет: не «nice-to-have флаггер», а необязательный→обязательный гейт наблюдаемости
E2 Слепой post-replace в русский выход ломает согласование (принудительная словарная форма в косвенный падеж; 46% ошибок constrained-моделей в en-cs — согласование, 2106.12398) С / 🟠 Запретить слепой регэксп-replace форм глоссария в ru-выход; форс только когда термин морфологически инвариантен ИЛИ есть полный decl И падеж резолвится; иначе флаг. Модели дают лемму в мягком глоссарии, склоняет она сама (+47.7 п.п. term accuracy в латышский — Bergmanis&Pinnis) DET post-check; политика форса; decl 1 🔶 decl — не «фича ru-рынка», а safety-механизм грамматичности
E3 Жёсткий constrained decoding как «нельзя» — переоценка: наивный (Grid Beam Search) стоит ~7 BLEU, но plug-and-play (Cascaded Beam Search, 2305.14538) даёт ~0 BLEU-потерь и EMA 0.925 на en-ru. Реальная причина не использовать — недоступен через закрытые frontier-API (нужен доступ к beam/декодеру), а не «портит качество» — / — Оставить soft-в-промпте дефолтом (доступно, качество подтверждено); формулировку research/05 conclusion #4 поправить DET 1 🔶 research/05 §5.3 «constrained decoding портит качество» — overstated

F. Инфраструктура и состояние (код backend/)

# Сценарий отказа Вер./Тяж. Механизм защиты Слой Где в коде/схеме Фаза Статус
F1 snapshotID не покрывает память → тихий расходящийся ре-пэй (D5.2). Подтверждено кодом: snapshotID (runner.go:145-163) хэширует brief/chunker/план-стадий/сэмплинг, но не версию approved-глоссария и не параметры сборки контекста; RequestHash (render.go:158) включает контент msgs; render.go:23-29 декларирует рендер «чистой функцией snapshot». Как только Фаза 1 инъектит память в msgs (render.go:133-136): изменённый глоссарий/STM меняет request_hash, но не snapshotID → resnapshot-гейт (runner.go:260) молчит → ре-ран старой главы промахивается мимо чекпоинта и переоплачивает расходящимся переводом, ломая инвариант Р6 В / 🔴🔇 Свернуть в snapshot ОБА: (1) состояние памяти (content-hash материализованного approved-глоссария + резюме/series-bible на момент старта джобы — рендер читает замороженную версию, не live; D8: content-hash, НЕ версия-счётчик — счётчик молча дрейфует, если забыли бампнуть) и (2) context-assembly-конфиг (glossary_injection, stm_depth, overlap). Тогда рендер снова чист по snapshot. УЖЕ СДЕЛАНО (commit 636919c): snapshot содержит context_assembly (GlossaryInjection/GlossaryTokenBudget) + MemoryVersion — материализует память → хеш (выбран мой рекомендованный «материализующий» вариант); Capability тоже свёрнут. memoryVersion() пока хеширует константу-заглушку tm-memory-v1\x00, т.к. банк памяти v2 ещё не приземлился (комментарий в коде явно фиксирует инвариант). DET runner.snapshotID()/memoryVersion()/contextSnap сделано; активируется с v2 структурно закрыто (636919c). ОСТАЁТСЯ узко: при приземлении глоссария заменить константу в memoryVersion() на хеш ORDER BY-стабильных материализованных строк (замороженных, не live) — иначе F1 тихо регрессирует.
F2 Токен-бюджет: eviction не определён. При превышении бюджета (matched + sticky > лимит) какая запись выпадает — не задано; выпавшая нужная запись = тихая деградация С / 🟡🔇 Явная политика приоритета/вытеснения (World Info inclusion-groups: approved>auto; персонажи чанка>фоновые) + логировать факт вытеснения (не молча) DET сборщик; retrieval-state запись 1 политика и лог вытеснения не специфицированы (находка критика)
F3 Конкурентность/порядок в SQLite: параллельный перевод чанков + запись auto-записей ломает first-translation-wins и sticky (оба предполагают последовательность); modernc — один писатель С / 🟡 Явное правило: auto-экстракция и коммит терминов — только на границе джоб (impl-notes §3.2 это уже фиксирует); sticky/порядок считаются от детерминированной пересборки чекпоинтов, не от wall-clock порядка DET граница джоб; write-пул store 1 🔶 назвать явно (находка критика)
F4 Один SQLite-файл на книгу = SPOF: повреждение файла молча теряет весь глоссарий+резюме Н / 🟠🔇 Бэкап/интеграл-чек/версионирование файла книги; экспорт TMX/TBX как побочный бэкап DET store; экспорт 2 🔶 нет строки бэкапа/интеграла (находка критика)
F5 Коррелированный отказ судьи-экстрактора: адъюдикация конфликтов и подтверждение кандидатов — одна модель на одних и тех же трудных кейсах (редкие имена, неоднозначный смысл) → систематически-неверный судья тихо промоутит плохие approved-ключи → retrieval-state lock-in С / 🟠🔇 Независимая проверка (судья чужого семейства / человек) на промоушене approved-ключа; first-encounter имя не промоутится в approved до батч-подтверждения раз в главу LLM(2-й)+HUMAN пайплайн подтверждения терминов 2 🔶 (находка критика; смыкается с A2/B1)

G. Популяция глоссария и человеческая ёмкость (корень и петля)

# Сценарий отказа Вер./Тяж. Механизм защиты Слой Где в коде/схеме Фаза Статус
G1 Качество автоэкстракции (bootstrap): все направления валидируют retrieval, но не популяцию. Мусор/пропуск главного персонажа на входе → горячий путь верно инъектит мусор / молча опускает — корневой отказ выше всего измеренного С / 🟠🔇 Разделить задачу экстракции на две (индикативно замерено, eval/extract_bench.py): (а) СПОТ кандидатов-спанов — локаль годится (qwen3-abliterated:8b recall 1.0 на zh+ja, 0 галлюцинаций, ~15с/чанк — дёшево на стенде); (б) РЕНДЕРинг dst — локаль НЕ годится (0.00.5: 阿Q→«А Цзы», 羅生門→«Россомаха», 秀才→«шоуцай» — как exp03), dst даёт сильная/облачная модель + человек. First-encounter unknown → status=auto/unconfirmed, гейт (батч-судья/человек) до промоушена в approved; alarm «имя-подобный токен без записи → неизвестная сущность» (DelTA-паттерн). ⚠ deepseek-v4-flash молча вернул пустой ответ на части чанков (祝福 воспроизводимо) — это тот empty-paid-response, что ловят D2/coverage-гейт DET(алярм)+LLM(спот=локаль / рендер=облако)+HUMAN pre-пасс NER; статус-машина термина 12 🔶 верифицировано (exp06, 13 моделей × 12 кейсов zh/ja/en): спот локальный OK (recall ~0.98 все языки), рендер локали язык-зависим (en 0.82 / ja 0.53 / zh 0.26) → спот=8b, рендер dst=облако(gemini-flash value)/человек, на zh + таблица Палладия B6
G2 Человеческая ёмкость — недоказанная петля всей safety-аргументации: почти каждая защита кончается «флаг → редактору/человеку». Объём флагов на главу никто не оценил; при высоком flag-rate «route to human» пусто → деградация тихая на практике С / 🟠🔇 Оценить flag-volume на главу на пилоте; alarm-пороги и aggregate «memory-coverage»-сигнал; безлюдный режим: auto→approved с записью в журнал (Р7), но с явной пометкой качества DET(телеметрия)+HUMAN retrieval-state запись; отчёт «паспорт качества главы» 12 flag-rate × throughput не смоделирован (находка критика)

Контракт горячего пути (снять противоречие до «где в коде»)

Направления разошлись в описании несущего компонента; резолюция:

  1. Горячий путь — НЕ FTS5. impl-notes §3.6 (строки 123-124) уже решает: «FTS5 в горячий путь v1 не тащить (unicode61 не сегментирует CJK; селективной инъекции хватает точных ключей/алиасов по обычным индексам)». То есть горячий путь — детерминированный multi-pattern матч (Aho-Corasick/double-array trie по src+алиасам над нормализованным чанком; для ru/en — по леммам/decl). FTS5/BM25 на горячем пути для zh/ja молча вернёт ничего (дефолтный токенайзер не сегментирует CJK) — их туда не ставить. Эмбеддинги на горячем пути — тоже нет (Р3). Это подтверждено эмпирикой (A3).
  2. sqlite-vec: причину отказа в Р3/research/05 поправить. «pure-Go не загрузит C-расширение» верно только для modernc.org/sqlite, не как свойство pure-Go: ncruces/go-sqlite3 (WASM) + asg017/sqlite-vec-go-bindings/ncruces — официально поддерживаемый CGO-free путь к SIMD-brute-force sqlite-vec. Решение (brute-force KNN в MVP) остаётся верным (эмпирика Q2: <20 мс на 100k×1024-d в чистом numpy; sqlite-vec-scalar 81108 мс на 1M — на порядки выше нужд одной книги), но рациональ переписать: реальная причина — «держим modernc единым драйвером / избегаем WASM-миграции», а не «pure-Go не может». ncruces+sqlite-vec — самый дешёвый триггер миграции, если вектора станут узким местом.

Гейты для бэкенда перед фиксацией схемы store/глоссария v2

Обязательно закрыть/учесть до заморозки схемы (шаг 4):

  1. F1 (snapshotID под память + context-assembly) — до инъекции Фазы 1. Класс бага «тихий расходящийся ре-пэй». Payload snapshot: версия-счётчик approved-глоссария + резюме/series-bible + glossary_injection/stm_depth/overlap; рендер читает pinned-версию.
  2. Схема v1 (подтверждаю D7) + добавить из этого реестра: поле sense (A3/B-полисемия); ось «редакционное время» отдельно от since_ch/until_ch (B1); констрейнты UNIQUE(src,sense,window) + обратная проверка dst-коллизий (B2); glossary.min_key_len per-язык + запрет одиночных ключей (A3); нормализационный артефакт NFKC/OpenCC/kana/ruby (A4).
  3. Post-check из E1/E2 — обязательный гейт, не опциональный флаггер; политика форса только при разрешимом падеже + decl.
  4. Трёхсторонний disposition инъекции (A2) + retrieval-state per-chunk запись (A1/F2/G2: n_exact_hits, n_sticky, n_ambiguous_flagged, n_spoiler_blocked, embedding_tier_used) — наблюдаемость деградации с первого дня.
  5. Отложено в v1.1/Фазу 2 (не блокирует v1-схему, но заложить поля/место): транслит-стандарты Палладий/Поливанов (B6), гейт фактичности резюме (D1), whole-book эмбеддинг-слой low-trust (A7/D2), бэкап/интеграл файла книги (F4), морфо-гейт глагольного рода (C3), измерение экстракции и flag-volume (G1/G2).

Ruby-seed контракт (вход шага 4 из шага 3a — подтверждён оркестратором 05.07)

Шаг 3a захватил ruby/фуригану в ruby_readings(book_id, base, reading, first_chapter, occurrences) (capture-only, не в промпте/hash). Маппинг в глоссарий v2 подтверждён, форма верна, с 3 уточнениями:

  • base (кандзи) → src; first_chaptersince_ch; occurrencesприор уверенности (не прямой approved).
  • reading (кана) → НЕ dst напрямую, а мост к правильному dst: это авторитетное произношение, по которому валидируется Поливанов-транслитерация. Ruby прямо усиливает пробел B6 — reading есть ground-truth для транслит-стандарта (без него B6 нерешаем; с ним — механически проверяем).
  • Уточнение 1 (обязательно): различать имя-чтение vs авторское двойное чтение (фуригана-игра). Ruby двойствен (04-unhappy §4): 鈴木→すずき (имя → name-lock, Поливанов) против 強敵→とも (база и чтение значат РАЗНОЕ — стилистический приём). Блайнд-лок второго (強敵→とも→«друг») = mistranslation. Классифицировать: фонетическая согласованность / паттерн собственного имени → name-lock; несвязанное чтение → переводческая сноска, не лок. Неоднозначное → человеку.
  • Уточнение 2: ruby — СИД в auto-кандидатный пул с приором occurrences, НЕ прямо в approved (статус-машина G1: высоко-частотное чтение → auto-кандидат; батч-судья/человек промоутит в approved до распространения на будущие главы).
  • Уточнение 3: decl/gender ruby НЕ даёт — они с term-commit LLM-пасса (decl) и контекста (gender). Ruby сидит src+reading→dst, остальное достраивается.

Перед реализацией прочитай research/13 §«Честные слабые места рекомендаций». Два пункта прямо влияют на этот реестр: F1 подан как решённый фикс, но там реальная развилка (snapshot материализует байты инъекции vs store версионирует глоссарий — я склоняюсь к «байты», но не доказал); E1 post-check — несущий гейт, но его надёжность в русской морфологии сама невалидирована (склонение-осознанный матч шумит → ложные флаги → редакторы игнорируют → safety рушится). Померить пост-check на русском ДО того, как доверять ему как гейту. Исполняемый эталон механизма (не продукт) — eval/memory_hotpath.py (T1T10 → Go-юниттесты).

In-house eval — обязателен (числа литературы не переносятся)

Ни один процитированный источник не меряет zh/ja→ru (DelTA/TransAgents/Karpinska/WMT25 — En-центричны или ru только как источник; вендорские memory-числа — про chat-память о пользователе, при честной методологии их бьёт ванильный RAG). Поэтому перед заморозкой ставки нужен свой замер на реальном стеке и направлении — детали, acceptance-критерии и протокол (bank-vs-long-context baseline, «random-terminology» адверсариальный arm из WMT25) — в docs/research/13-memory-bank-validation.md §Вердикт. Реестр рисков не заменяет этот замер — он делает провалы явными, что и есть ответ на опасение владельца: тихую деградацию мы конвертируем в громкую (post-check + disposition-лог + retrieval-state), а не устраняем её обещанием.