textmachine/docs/prompts/done/BACKEND_SESSION_PROMPT_MEMORY.md

36 KiB
Raw Blame History

Промт сессии «Бэкенд — Шаг 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), таблицы AG (каждая строка — механизм или дыра со статусом /🔶/).
    • docs/research/13-memory-bank-validation.md §«Честные слабые места рекомендаций» — ОБЯЗАТЕЛЬНО. Два несущих НЕдоказанных места: F1 (snapshot материализует байты инъекции vs store версионирует глоссарий) и E1 post-check (надёжность в русской морфологии невалидирована — склонение-осознанный матч шумит → ложные флаги → редакторы игнорируют → safety рушится). Померь post-check на русском ДО того, как доверять ему как жёсткому гейту.
    • docs/architecture/05-decisions-log.mdD7 (схема памяти 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.goRenderVars{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.gosnapshotID() уже сворачивает 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.goRubyReadingsForBook (сид auto-кандидатов).
    • config/pipeline.goContextAssembly(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 (T1T10 → 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: персонажи прошлых 12 чанков — состояние из детерминированной пересборки чекпоинтов, НЕ эмбеддинги). Трёхсторонний 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/лемматизатор) есть в выходе; нет → флаг/точечный ре-перевод. Ловит и «модель проигнорировала (1736% терминов)», и «мы инъектили неверное». ЗАПРЕТ слепого post-replace форм глоссария в ru-выход (E2): форс только когда термин морфологически инвариантен ИЛИ полный decl И падеж резолвится; иначе флаг (модели даём лемму, склоняет сама). decl — safety-механизм грамматичности. ⚠ Померь надёжность post-check на русском (E1, невалидирована) ДО жёсткого гейта (порт eval/memory_hotpath.py T1T10 в Go); шумит → флаг-режим, не форс.

Ruby-seed (вход из 3a, реестр §Ruby-seed, подтверждён оркестратором)

basesrc; first_chaptersince_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 памяти); регрессии (Вехи 02.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 (T1T10 порт + русская морфология), memoryVersion-материализация (смена approved → --resnapshot), e2e инъекция→мок-перевод→post-check, resume $0. Файлы БД только на ext4 (не /mnt/c).

Открытые (уточни ДО кода, если стоят переделки):

  • Граница первой вехи: только AE (схема+горячий путь+инъекция+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-строк.
  • Граница первой вехи: ПОДТВЕРЖДАЮ AE на 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 даёт 1836% ЛОЖНЫХ флагов на трудном чанке (deepseek 2/11, grok 4/11 — склонённые формы, отрендеренные ВЕРНО, которые \b-регэксп теряет: «Вэйчжуане», «Бородатого Вана», «Дэна», «сюцая»). → Порт memory_hotpath.py T1T10 «как есть» = порт РЕГЭКСПА = этот шум. Не порти 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 — и держи тихую деградацию громкой.