# Промт сессии «Бэкенд — Шаг 4: банк памяти v2 (глоссарий + инъекционная поверхность + горячий путь)»

Ты — инженер-бэкенд проекта **TextMachine** (Go, бэкенд AI-перевода крупных художественных текстов ранобэ/вебновелл, zh/ja/en→ru, мультиагентный пайплайн). Рабочий каталог `/home/ubuntu/projects/textmachine`, твоя зона — `backend/`. Это **продолжающая** сессия: Фаза 0 (каркас), Веха 1 (capability + snapshotID), Веха 2 (chunk-loop + disposition), Веха 2.5 (coverage-гейт + single-hop эскалация + D12) и **шаг 3a** (настоящий чанкер + импорт txt/epub + ruby-захват; принят внешним ревью, все 7 находок закрыты) сделаны и закоммичены. Ты **не начинаешь с нуля и не переписываешь сделанное** — сначала валидируешь точку старта, потом строишь.

Метод жёсткий, без хаков: **доки+код → валидация старта → код в ратифицированном порядке → агентское адверсариальное селфревью на вехе → сдаёшь на внешнее ревью.** Никаких срезов углов, закомментированных проверок, обходов инварианта детерминизма, «потом починю». Упёрся в двусмысленность контракта, которая стоит переделки — короткий вопрос владельцу/оркестратору ДО кода, не выдумывай.

Память v2 — **главный блокер качества перевода** (консистентность имён/терминов — жалоба №1 читателей, 04-unhappy §1). Это самый рискованный шаг: тихая деградация тут — не «пусто», а **ошибочная инъекция**, которой сильная модель послушно следует (уверенность растёт, точность падает — arXiv 2510.00829). Реестр рисков `architecture/06` — твоё ТЗ по механизмам защиты; читай его дословно.

---

## 0. ПЕРВОЕ ДЕЙСТВИЕ — валидация точки старта (обязательно, до любого кода)

1. **git:** `git -C /home/ubuntu/projects/textmachine log --oneline -8`. SHA плавают (параллельные сессии). Последний backend-код-коммит — **«Close step-3a external-review findings…»** (7 находок 3a закрыты: spine-reconciliation, UTF-8-нормализация, quote-depth, ruby full-replace, tiling-fuzz). Рабочее дерево по `backend/` чистое (грязные `eval/`, `docs/experiments|research/*` — зоны Полигона/памяти, НЕ трогать/НЕ коммитить).
2. **Сборка/тесты** (Go 1.26.4 в `~/sdk/go`; из `backend/`): `export PATH="$HOME/sdk/go/bin:$PATH"; go build ./... && go vet ./... && go test ./... -race -count=1`. Всё зелёное. Не зелёное — находка, разбирайся до кода. (cwd Bash может сбрасываться — заходи в `backend/` явно; пример проекта — `backend/example/`, НЕ repo-root.)
3. **Прочитай доки (источник истины, при конфликте приоритет у первого):**
   - `docs/PROGRESS.md` §**Бэкенд** (последние записи: шаг 3a + закрытие 7 находок) и §**Память** (журнал памяти-сессии — вердикт+реестр).
   - **`docs/architecture/06-memory-risk-registry.md` — ЦЕНТРАЛЬНЫЙ вход, читай дословно.** §«Гейты для бэкенда перед фиксацией схемы store/глоссария v2» (что обязан закрыть до заморозки схемы), §«Контракт горячего пути» (НЕ FTS5, НЕ вектор — детерминированный multi-pattern матч), §«Ruby-seed контракт» (вход из 3a), таблицы A–G (каждая строка — механизм или дыра со статусом ✅/🔶/❌).
   - **`docs/research/13-memory-bank-validation.md` §«Честные слабые места рекомендаций»** — ОБЯЗАТЕЛЬНО. Два несущих НЕдоказанных места: **F1** (snapshot материализует байты инъекции vs store версионирует глоссарий) и **E1 post-check** (надёжность в русской морфологии невалидирована — склонение-осознанный матч шумит → ложные флаги → редакторы игнорируют → safety рушится). Померь post-check на русском ДО того, как доверять ему как жёсткому гейту.
   - `docs/architecture/05-decisions-log.md` — **D7** (схема памяти v1: `src,dst,type,aliases,gender,speech,decl,since_ch/until_ch,status,note` + alias-граф + `gender=hidden` + `translit_policy` + `first_person` + series-bible-lite = gender+ты/вы-журнал; `nickname_translation` в v1), **D8** (memory-компонент snapshotID = content-hash материализации, не version-counter), **D10** (знаменатель консистентности = ВСЕ approved-термины, порог 98% на детерминированном слое), **D5.1/D5.2** (проактивный `gender=hidden`; snapshotID покрывает память).
   - `docs/architecture/03-implementation-notes.md` — §3.2 (snapshot: замороженная версия approved-глоссария; **STM в snapshot НЕ входит**; батч-подтверждение терминов только на границе джоб), §3.6 (SQLite: **FTS5 в горячий путь НЕ тащить**; кириллица нормализуется в Go), §3.7 (coverage-гейт), §3.8 (граница Р2: режим инъекции/бюджеты — конфиг; сборка/матч — код).
   - `docs/architecture/04-unhappy-paths.md` §1/§2/§4 + «Следствия» п.2; `02-mvp-plan.md` «Фаза 1» (банк памяти + приёмка ≥98%); `01-decisions.md` Р3/Р5/Р6 (не редактировать).
4. **Прочитай код, который расширяешь (инварианты не ломать):**
   - `pipeline/render.go` — **`RenderVars{Book,Text,Draft}` — ФИКС-структура**, `Render` падает на неизвестном `{{…}}`; `Messages()`=`[system(CacheBoundary:true), user]`, **инъекция глоссария/STM встаёт МЕЖДУ ними** (комментарий в коде это фиксирует), Р5-раскладка. `memoryVersion()` — **сейчас хеширует КОНСТАНТУ `tm-memory-v1\x00`** (заглушка; **твоя задача — заменить на хеш ORDER BY-стабильных ЗАМОРОЖЕННЫХ approved-строк**, F1). `RequestHash` включает контент `msgs`; `NormalizeSource`; `EstimateTokens`.
   - `pipeline/runner.go` — `snapshotID()` уже сворачивает `contextSnap`(glossary_injection/budget/stm/overlap/ttl) + `MemoryVersion` (F1 структурно, 5eaf2d9 — активируется твоей материализацией); `translateChunk`/`runStage`/`runAttempt` (цикл стадий — **семантику НЕ меняешь**); `persistRuby` (шаг 3a — `ReplaceRubyReadings`, полная замена агрегата). Инъекция память→msgs встаёт в `runStage` перед `Messages()`.
   - `pipeline/chunker.go`/`ingest.go` — источник `Chunk.Text` и `ruby_readings` (твой сид). Не трогаешь, потребляешь.
   - `store/migrate.go` — миграции v1→v4 (v4 = `ruby_readings`). Твоя — **v5** (append-only, IF NOT EXISTS, tx на шаг, **никогда не редактируй прошлый шаг**). `store/ruby.go` — `RubyReadingsForBook` (сид auto-кандидатов).
   - `config/pipeline.go` — `ContextAssembly`(glossary_injection `selective|full_prefix`, glossary_token_budget, stm_depth, overlap_tokens, cache_ttl) — схема есть, инъекции нет.
5. **Прогони** `tmctl report --config example/book.yaml` (из `backend/`) — $0, LLM не зовётся. (Протухший `example/*.db*` — gitignored, удали, пересоздастся — schema бампнута v4→после смены `chunkerVersion` пример пере-снапшотится.)
6. **Исполнимый эталон (зона Полигона — читать, НЕ редактировать/коммитить):** `eval/memory_hotpath.py` (T1–T10 → Go-юниттесты), `eval/retrieval_bench.py`+`eval/data/retrieval_bench/` (точный многосимвольный подстрочный матч precision 1.0; фаззи/одиночные ключи — все ловушки в top-5), `eval/extract_bench.py` (спот=локаль OK, рендер dst=облако/человек).

**Инвариант, который держишь свято:** любой инпут, меняющий wire-запрос ИЛИ `max_tokens`, обязан входить либо в snapshot, либо в `RequestHash`. **Материализованная память (approved-глоссарий/резюме/series-bible на момент старта джобы) обязана войти в `memoryVersion()` → snapshot;** рендер читает **замороженную** версию, не live (F1 — 🔴🔇, класс D5.2). STM в snapshot НЕ входит (пересобирается из чекпоинтов).

---

## 1. Состояние на входе (что готово — НЕ пересоздавать)

- **Chunk-loop + disposition + coverage + escalation (Вехи 2/2.5), чанкер+ingest+ruby (3a, 7 находок закрыты)** — семантику не трогаешь, потребляешь.
- **snapshotID уже сворачивает `context_assembly` + `MemoryVersion` (F1 структурно, 5eaf2d9):** остаётся **заменить константу в `memoryVersion()` на хеш материализации** — иначе F1 тихо регрессирует.
- **`ruby_readings` (v4):** capture-only, полная замена агрегата (не в промпте/hash). **Твой сид auto-кандидатов** (не approved напрямую).
- **Config-ручки `context.*`** заведены, но в `msgs` НЕ инъектятся — твоя работа.
- **`RenderVars` — фикс-структура**, `Render` падает на неизвестном `{{…}}` (инъекция = НЕ template-плейсхолдер по умолчанию; см. §3).

---

## 2. МИССИЯ — банк памяти v2 (предлагаемая первая веха; полный скоуп согласуй в §8)

Дать пайплайну **детерминированную селективную инъекцию approved-глоссария в горячий путь + обязательный post-check**, построив инъекционную msgs-поверхность ОДИН раз (её же ждут отложенные overlap-инъекция и ruby-лок шага 3a). Ратифицированный порядок реестра (§«Гейты перед фиксацией схемы»):

### A. Схема store v2 (миграция v5) — заморозка глоссария
База D7 + **добавь из реестра:** поле `sense` (A3/полисемия); **ось «редакционное время» ОТДЕЛЬНО от `since_ch/until_ch`** (B1 stale — append-only журнал ревизий рендеринга); `UNIQUE(src,sense,window)` + обратная проверка `dst`-коллизий инъективности (B2); `min_key_len` per-язык + **запрет одиночного Han/каны/латинской буквы как самостоятельного ключа** (A3, schema-констрейнт); статус-машина термина (`auto|draft|approved` + журнал). **Нормализационный артефакт (A4, ❌ самый вероятный «тихо пусто»):** NFKC + OpenCC упрощ./трад. + kana-folding + захват ruby-чтений, **симметрично к ключам/алиасам И к тексту чанка**; версионированный, тестируемый, детерминированный. Кириллица нормализуется в Go до записи (§3.6).

### B. Горячий путь — детерминированный матчер (НЕ FTS5, НЕ вектор, НЕ эмбеддинги)
Multi-pattern матч (Aho-Corasick / double-array trie) по `src`+алиасам над **нормализованным** чанком; для ru/en — по леммам/`decl`. Longest-match, замена целой сущности. **«Лучше ничего, чем мусор» (A1):** пустой матч — норма/безопасно; бюджет допустимо недозаполнить; НЕ добивать соседями; **точность > полнота.** Sticky scene-inertia (A5: персонажи прошлых 1–2 чанков — состояние из детерминированной пересборки чекпоинтов, НЕ эмбеддинги). **Трёхсторонний disposition инъекции (A2, ❌):** `CONFIRMED`→авторитетно; `AMBIGUOUS` (короткий/общий алиас, коллизия поверхности, auto/draft)→инъекция с пометкой «unverified» **+ принудительный post-check**; `REJECT`→выкинуть+залогировать; каждая ветка наблюдаема. **Спойлер-окно `since_ch/until_ch` = жёсткий reject-гейт с логируемым событием (C1, safety, не UX-фича).** **`retrieval-state` per-chunk:** `n_exact_hits, n_sticky, n_ambiguous_flagged, n_spoiler_blocked, embedding_tier_used(=0 в v1)`.

### C. Инъекционная msgs-поверхность (строим ОДИН раз, Р5)
Раскладка `system(стабильный префикс, кэш-граница) → [глоссарий/STM] → user(волатильный хвост: чанк/черновик)` (A6: авторитетный блок глоссария рядом с чанком, не в середине стопки резюме). **Селективный глоссарий в волатильном хвосте не ломает DeepSeek-префикс-кэш** (Р5). Это **та же поверхность, что ждут отложенные overlap-инъекция и ruby→глоссарий-лок 3a** — строишь один раз, они встают на неё.

### D. memoryVersion() — закрыть F1
Замени константу на **хеш ORDER BY-стабильных ЗАМОРОЖЕННЫХ approved-строк** (версия approved-глоссария + резюме/series-bible на старте джобы). Рендер читает pinned-версию из snapshot, не live. Смена approved → громкий `--resnapshot`.

### E. Post-check — ОБЯЗАТЕЛЬНЫЙ гейт (E1/E2, не опциональный флаггер)
Для каждого approved-термина с `src` в чанке — проверить, что **склонённая** форма `dst` (через `decl`/лемматизатор) есть в выходе; нет → флаг/точечный ре-перевод. Ловит и «модель проигнорировала (17–36% терминов)», и «мы инъектили неверное». **ЗАПРЕТ слепого post-replace форм глоссария в ru-выход (E2):** форс только когда термин морфологически инвариантен ИЛИ полный `decl` И падеж резолвится; иначе флаг (модели даём лемму, склоняет сама). `decl` — safety-механизм грамматичности. **⚠ Померь надёжность post-check на русском (E1, невалидирована) ДО жёсткого гейта** (порт `eval/memory_hotpath.py` T1–T10 в Go); шумит → флаг-режим, не форс.

### Ruby-seed (вход из 3a, реестр §Ruby-seed, подтверждён оркестратором)
`base`→`src`; `first_chapter`→`since_ch`; `occurrences`→**приор уверенности** (НЕ approved напрямую). `reading`→**НЕ dst напрямую, а мост к Поливанов-dst-валидации** (усиливает B6). **Различай имя-чтение vs авторское двойное чтение:** 鈴木→すずき (name-lock, Поливанов) против 強敵→とも (значат РАЗНОЕ → **сноска, не лок**; блайнд-лок = mistranslation); неоднозначное → человеку. Ruby — **СИД в auto-кандидатный пул**, промоушен в approved батч-судьёй/человеком (G1). (Ruby-хранилище 3a уже делает полную замену агрегата — фантомов нет.)

---

## 3. Шов, который сохраняешь (контракт цикла и рендера)

- **`RenderVars{Book,Text,Draft}` — фикс-структура; `Render` падает на неизвестном `{{…}}`.** Инъекция = НЕ template-плейсхолдер по умолчанию. Два чистых пути: (а) **код-собранный injection-message** в `Messages()` из матченных записей (предпочтительно — без правки шаблонов), ИЛИ (б) новое поле `RenderVars` + плейсхолдер + правка `Messages()`. Реши по Р5; кэш-граница остаётся на стабильном префиксе.
- **`ch.Text` в ТРЁХ местах (3a-контракт):** `{{text}}` на проводе, source coverage-гейта, `RequestHash`. Инъекция — ОТДЕЛЬНЫЙ блок, НЕ в `ch.Text` (иначе течёт в coverage len_ratio и хеш).
- **LLM только батч/граница джоб**, НИКОГДА в горячем пути инъекции (F3/§3.2). Цикл последовательный (D12: гонка `Runner.clients` + коммит терминов только на границе джоб — наивный параллелизм чанков НЕ строить).
- **Смена материализованной памяти → `memoryVersion()` → snapshot → громкий `--resnapshot`** (не тихий промах, F1).

## 4. Что НЕ делать в шаге 4 (отложено — реестр §5, не блокирует v1-схему; заложи поля/место)

- Эмбеддинги/вектор на горячем пути (A7/D2) — Фаза 2, low-trust; sqlite-vec не сейчас.
- Транслит-стандарты Палладий/Поливанов post-check (B6) — поле/место в v1, валидатор — Фаза 2 (reading как мост заложи).
- Гейт фактичности резюме (D1, ❌ первоклассный пробел), морфо-гейт глагольного рода (C3) — Python-сайдкар Фазы 2.
- Whole-book RAG по резюме (D2), бэкап/интеграл файла книги (F4) — Фаза 2.
- Автоэкстракция/адъюдикация конфликтов/батч-подтверждение (G1/B2/F5), резюме глава→арка→книга, series-bible-lite — если не влезает в первую веху, **отдельная веха** (спот=локаль / рендер dst=облако+человек; экстракция — LLM только на границе джоб, НЕ в горячем пути). Согласуй границу вехи (§8).

Если автопопуляция окажется критична для приёмки раньше — **короткий вопрос оркестратору**, не тащи молча.

## 5. Инварианты (жёстко)

- **Детерминизм священен.** Матч/инъекция/post-check/нормализация — чистые детерминированные функции; `memoryVersion()` = хеш замороженной материализации в snapshot; нормализация — версионированный артефакт; никаких map-итераций в порядке вывода, time/rand.
- **Тихую деградацию конвертируем в громкую, а не устраняем обещанием:** post-check + disposition-лог + retrieval-state. Пустой retrieval — норма; **ошибочная инъекция — главный враг** (A2).
- **Деньги.** Горячий путь — $0 (без LLM). LLM-экстракция/адъюдикация — батч на границе джоб, покрыта reserve/settle/эскалацией/F3 — не сломай.
- **Границы Р2/Р3.** Матч/сборка — код; режим инъекции/бюджеты/TTL — конфиг. Кириллица нормализуется в Go. FTS5/вектор — НЕ на горячий путь.
- **disposition/coverage/escalation Вех 2/2.5 не ломать.**

## 6. Правила проекта

- Метод: доки+код → валидация → код → **агентское селфревью на вехе** (адверсариальный многоагентный Workflow: дименсии → находки → независимая верификация CONFIRMED/REFUTED с конкретным сценарием инпут→неверный-выход → исправь подтверждённые + регресс-тесты **мутационно-проверенные**: сломай — тест должен упасть) → сдаёшь оркестратору/владельцу.
- **Дименсии селфревью шага 4 (минимум):** детерминизм (матч/инъекция/post-check/`memoryVersion` — F1 не регрессирует); нормализация/матч (A3/A4 — одиночные ключи запрещены, симметрия ключ↔текст, 0 ложных на ловушках `retrieval_bench`); disposition инъекции (A2 три ветки, AMBIGUOUS→post-check); спойлер (C1 жёсткий reject); post-check (E1/E2 — форс только при резолвимом падеже, слепой replace запрещён, русская морфология померена); интеграция (msgs-поверхность Р5, `ch.Text` чист, кэш-граница цела, snapshot-fold памяти); регрессии (Вехи 0–2.5 + 3a целы; e2e инъекция→перевод→post-check; resume $0; ре-снапшот при смене approved).
- **Не редактировать** `docs/research/*`, `01/02-decisions`, `06-memory-risk-registry` — правки только через `03-implementation-notes.md` и `PROGRESS.md` (§Бэкенд). **Не трогать/не коммитить** `eval/`, `docs/experiments|research/*` (читать можно). **Не коммитить** `settings.local.json`, `*.db`.
- Коммиты: одно предложение на английском ≤30 слов, без трейлера `Co-Authored-By`, без многострочного тела. На `main` прямо, только `backend/` + `PROGRESS`, никогда `eval/`. PUML не рендерить в картинки. Автопамять (`MEMORY.md`) — сверься.

## 7. Риски интеграции (проверено картой кода)

- **(F1) `memoryVersion()` — константа-заглушка.** Заменишь на хеш материализации — иначе смена approved-глоссария молча промахивается мимо resnapshot-гейта и переоплачивает расходящимся переводом (🔴🔇). Материализуй **замороженные** строки (pinned), не live.
- **(A4) нормализация — самый вероятный «тихо пусто».** Ключ есть в тексте, но в другой форме (упрощ./трад., полу/полноширина, кандзи↔кана, ruby). Симметричная нормализация ключа И текста — версионированный артефакт.
- **(A2/A3) ошибочная инъекция — главный источник тихой деградации.** Одиночный CJK/короткий алиас срабатывает на чужом смысле (阿Q, 炎=пламя vs имя 萧炎). Запрет одиночных ключей + longest-match + трёхсторонний disposition.
- **(E1) post-check в русской морфологии невалидирован.** Склонение-осознанный матч шумит. Померь ДО жёсткого гейта; шумит → флаг, не форс.
- **(Р5/кэш) инъекция между system и user** не должна сдвигать кэш-границу со стабильного префикса (иначе DeepSeek-автокэш промахивается, Anthropic `cache_control` не там).

## 8. Сборка/прогон + открытые вопросы

Из `backend/`: `go build/vet/test -race -count=1` — зелёные на каждом шаге. Тесты: нормализация (A4, версионированная), матчер (longest-match/одиночные запрещены/ловушки `retrieval_bench` 0 ложных), disposition (3 ветки), спойлер-reject, post-check (T1–T10 порт + **русская морфология**), memoryVersion-материализация (смена approved → `--resnapshot`), e2e инъекция→мок-перевод→post-check, resume $0. Файлы БД только на ext4 (не /mnt/c).

**Открытые (уточни ДО кода, если стоят переделки):**
- **Граница первой вехи:** только A–E (схема+горячий путь+инъекция+F1+post-check) на РУЧНОМ/ruby-сиде глоссарии, а автопопуляцию/адъюдикацию/резюме/series-bible — в следующие вехи? (Рекомендация: да — поверхность+post-check+F1 разблокируют качество и отложенные overlap/ruby-лок; популяция — отдельно.)
- **F1-развилка (research/13 §слабые места):** snapshot материализует БАЙТЫ инъекции vs store версионирует глоссарий-счётчиком. 5eaf2d9 выбрал материализующий вариант — следуй ему (хеш замороженных строк); подтверди, если сомнение.
- **Post-check как гейт vs флаггер** до замера русской морфологии (E1) — жёсткий гейт только после валидации precision.
- **Раскладка инъекции** (код-message vs RenderVars-поле) и кэш-граница Р5 — реши по §3, зафиксируй в PROGRESS.
- **Ruby: классификатор имя-чтение vs авторское двойное чтение** — детерминированное правило (фонетическая согласованность/паттерн имени) vs флаг человеку; согласуй порог.

### Оркестратор ответил на §8 (05.07) — можешь стартовать с этими ответами

- **F1-развилка: ПОДТВЕРЖДАЮ материализацию** (5eaf2d9). Это ровно **D8** (content-hash материализации, не version-counter — drift-proof: забыть бампнуть нельзя). Следуй; хеш ORDER BY-стабильных ЗАМОРОЖЕННЫХ approved-строк.
- **Граница первой вехи: ПОДТВЕРЖДАЮ A–E на seeded-глоссарии** (ручной + ruby-сид); автопопуляция/адъюдикация/резюме/series-bible — отдельные вехи. **Уточнение приёмки:** порог **≥98% консистентности (D10/02-mvp) осмыслен только на ПОПУЛИРОВАННОМ глоссарии** — приёмка ПЕРВОЙ вехи = «механизм корректен на seeded-глоссарии» (инъекция/disposition/post-check/F1 работают, 0 ложных на ловушках `retrieval_bench`, resnapshot при смене approved), НЕ «≥98% на реальной книге». Это инфраструктурная веха, как Фаза 1.
- **Post-check: ПОДТВЕРЖДАЮ флаггер-до-замера** (жёсткий гейт только после валидации precision на русском). **Уточнение:** Фаза-1 post-check матчит против **записанных `decl`-форм** (Go, детерминированно; `decl` заполнен при term-commit одним дешёвым LLM-вызовом), **НЕ живой лемматизатор** (тот — Python-сайдкар Фазы 2, C3). E1-замер = precision матча `decl`-форм на реальном русском; шумит → флаг-режим, не форс.
- **Раскладка инъекции: код-собранный injection-message в `Messages()`** (вариант «а»), НЕ новый `RenderVars`-плейсхолдер: держит шаблон стабильным (`Render` падает на неизвестном `{{}}`), инъекция = динамический per-chunk контент вне фикс-шаблона, кэш-граница остаётся на стабильном system-префиксе (Р5).
- **Ruby-классификатор: детерминированное правило для ЯСНЫХ случаев** (фонетическая согласованность reading↔base / паттерн собственного имени → name-lock), **неоднозначное → флаг человеку**. Over-flag безопасно, блайнд-лок = mistranslation — зеркалит «точность > полнота» горячего пути. Не умничать: сомнение → флаг.

### research/14 (сессия «адаптивный слой памяти») — вход для шага 4 (оркестратор, 05.07)

Сессия исследования гибрида завершена (`docs/research/14-adaptive-memory.md`) — она **не меняет твой скоуп** (обучающийся слой отклонён: kNN-MT/frontier-LoRA физически blocked живой пробой; демонстрации — пилот Ф2.5, не сейчас; твоя работа — чисто детерминированная, это подтверждено). Но она **эмпирически измерила** несущий риск E1, и это меняет, как ты к нему подходишь:

- **E1 КОЛИЧЕСТВЕННО (главное).** Наивный регэксп-post-check даёт **18–36% ЛОЖНЫХ флагов** на трудном чанке (deepseek 2/11, grok 4/11 — склонённые формы, отрендеренные ВЕРНО, которые `\b`-регэксп теряет: «Вэйчжуане», «Бородатого Вана», «Дэна», «сюцая»). → **Порт `memory_hotpath.py` T1–T10 «как есть» = порт РЕГЭКСПА = этот шум.** Не порти accept-регэкспы дословно; матч обязан быть decl-осознанным (подтверждает выбор §8 «против записанных `decl`-форм»).
- **Но stored-decl не бесплатно надёжен.** Надёжность = функция **ПОЛНОТЫ** записанных `decl`-форм. Для OOV-транслит-имён склонение нетривиально (C4) → один дешёвый LLM-вызов при term-commit может недозаполнить падежи → те же ложные флаги. **Замеряй false-flag rate именно stored-decl-матчера на реальном СКЛОНЁННОМ русском выходе, не «примерно»** (это и есть E1-замер §0/§8 «до жёсткого гейта»).
- **Фолбэк де-рискнут (хорошая новость research/13 §2).** pymorphy3 (Python-сайдкар Ф2, C3) эмпирически **сворачивает OOV-транслит-склонение** (Вана→ван, Дэна→дэн, Вэйчжуане→вэйчжуан, Цзоу→цзоу). В Go сейчас НЕ тащи (нет годного pure-Go рус-лемматизатора — это и есть причина stored-decl в Ф1), но знай: если stored-decl шумит на замере — лемматизатор-сайдкар Ф2 валидированный фолбэк.
- **Готовые ассеты E1-замера** (зона Полигона — читать/портировать МЕТРИКУ в Go, не редактировать/коммитить): `eval/adaptive_reask.py` (strict-регэксп vs лемма на реальном zh→ru, канон Рогова — диагностика ложных флагов), `eval/adaptive_levers.py` (кривая L1-лемма-post-check / L2-recall-норм+alias+sticky / L3-re-ask на трудном чанке 11 сущностей). Это ровно тот «замер русской морфологии», что требует §8.
- **A3 подтверждён кейсом.** `钱` (односимвольный ключ, гомограф Цянь-фамилия / 钱=деньги) упорно матчится/инъектится не туда даже после re-ask → конкретный кейс в тест-корпус `min_key_len`/запрета одиночных ключей.
- **A4 подтверждён живым багом.** Неполная trad-карта (у меня не было `爺→爷`) **молча промахнула** 赵太爷 → это ровно «тихо пусто» A4. **Полный OpenCC, не мини-карта**; трап-корпус матчера (по образцу `retrieval_bench`) должен включать trad-варианты И склонённые ru-формы, иначе тест зелёный, а бой промахивается.
- **Подтверждение подхода (не расслабляйся).** Soft-глоссарий на реальном zh→ru индикативно тянет 0.458→~1.0 канон-консистентности (валидация банка этого на нашем направлении не мерила) — детерминированная инъекция на zh→ru эмпирически не пуста. Чанк был «лёгкий» (насытился); реальную кривую L1→L3 меряй на трудных чанках.

Welcome back to the backend. Валидируй старт, заморозь схему по реестру, построй горячий путь и инъекционную поверхность ОДИН раз, закрой F1 и post-check — и держи тихую деградацию громкой.
