textmachine/docs/archive/prompts/BACKEND_TRACKA_PACK1_SESSION_PROMPT_2026-07-16.md

17 KiB
Raw Blame History

⟶ ОТРАБОТАН, ЗАЛЕНДЕН d3f6b34, ратифицирован D39.2 (16.07). T1T4 исполнены; адверсариал-долг 529 погашен постфактум (D39.4). Архивная копия.

Промт: сессия-БЭКЕНД — Трек A, пак-1 (структурная гигиена + память-корень + export-contract), 2026-07-13

Рождён из арх-ресета D39 (docs/architecture/05-decisions-log.md) и синк-аудита (docs/architecture/08-sync-audit-ledger.md, 09-target-architecture.md). Это build-now трек: чистые фиксы, ресёрч НЕ нужен. Параллельно идёт трек B (полигон-ресёрч нарезки/когезии) — не твоя зона, не трогай eval/.

Кто ты и зона

Бэкенд-сессия TextMachine (Go-пайплайн издательского перевода zh/ja/en→ru). Зона записи — backend/ только. Читать можно всё; расхождения — пингом оркестратору в docs/PROGRESS.md (не правь чужие зоны). Сессия не коммитит — код ревьюит и лендит оркестратор после приёмки.

Онбординг (порядок)

  1. CLAUDE.md (гардрейлы) → docs/architecture/09-target-architecture.md (целевые слои, §2 слои 4/6/7/2-сеам, §4 трек A) → docs/architecture/08-sync-audit-ledger.md (находки этого пака: L3-*, L5-normalization-fused-*, L8-*).
  2. docs/architecture/05-decisions-log.md: D39 (арх-ресет), D30.1 (редактор БИЛИНГВ), D2 (диспозиции), D8/D24.4 (снапшот/база dst), D30.3/D33.1/D35.4a (санитайзер/strip-export), D38.3 (инфра-пак).
  3. docs/experiments/00-provider-quirks.md (ПЕРЕД любыми правками, если тронешь вызовы — тут не должно, но правило).
  4. backend/README.md + релевантный код (см. задачи).

ЖЁСТКИЕ гардрейлы (из CLAUDE.md)

  • НИКОГДА не читать .env. 18+ уровень 3 — не открывать eval/data/*corpus*, /home/ubuntu/books/ без прямой задачи (тут не нужно).
  • Снапшот-дисциплина = деньги. Любая правка, меняющая рендер запроса / выбор инъекции / текст выхода → меняет снапшот (Р6). Это ГРОМКИЙ --resnapshot (переоплата книги). Ты НЕ пере-прогоняешь книгу (это гейтится треком B). Твоя обязанность: (а) правильно fold-ить новую версию поведения в снапшот (render.go/snapshot.go, паттерн coverageSnap/sanitizerSnap); (б) обновить golden-фикстуры (internal/pipeline/testdata/golden/) под новое поведение — golden = инвариант №8 (D23), обновление только под ратифицированную D39 смену поведения (она ратифицирована — этот пак); (в) в отчёте перечислить, какие снапшот-версии сдвинулись.
  • Мандат самопроверки (решение владельца, CLAUDE.md): после кода — ЯВНЫЙ бэкенд-ревью ИСПОЛНЕНИЕМ: go build/vet/gofmt/test -race ./... зелёные; каждый новый класс/фикс запинен РЕАЛЬНЫМ тестом (firing + non-firing/FP-guard); каждую нетривиальную правку прогони на сконструированном входе и покажи ДО/ПОСЛЕ. Полигон/бэкенд регулярно багуют — не на веру.
  • Git-дисциплина мультисессий: НЕ коммить; git status — твои правки только в backend/. Не трогай START_PROMT.MD, docs/, eval/.

ЗАДАЧИ (4; независимы, порядок по ROI/риску)

T1 — Память: код-корень терм-дрейфа (слой 4) · находки L3-seed-workaround-not-code-root, L3-recurrence-*, L3-suppressor-disposition-blind-right-fix, L3-editor-block-stale-monolingual-D1

Проблема (CONFIRMED по коду): терм-дрейф «починили» промоутом сида (D38.5), а код-корень жив. suppressContained (memory.go:615-641) disposition-слепой: validSuppressor гейтит только на spoilerBlocked, НЕ на доверии → более длинный draft/ambiguous ключ подавляет вложенный approved (напр. draft 四代族长 съедает approved 族长), а сам не инъектится (editor-блок CONFIRMED-only, memory.go:489) → читатель получает ничего. Хуже: подавленный термин не попадает в memSel.injected → post-check и retrieval-state его НЕ видят (дроп тихий И нелогируемый — нарушает обещание research/13 «тихую деградацию → в громкую»).

Сделать:

  1. Disposition-gate на suppressor. validSuppressor должен требовать, чтобы более длинный матч был не ниже по доверию, чем вложенный, который он подавляет: суппрессор с dispositionFor = AMBIGUOUS (draft/auto/collision-prone) не подавляет вложенный, чей dispositionFor = CONFIRMED (approved). Сохрани верный longest-match, когда длинный ≥ доверия короткого (approved 四代族长 по-прежнему бьёт approved 族长). Реши краевые (equal-length, оба draft — как сейчас). Спот-верифицируй, что промоутнутые D38.5 термы теперь работали бы и БЕЗ промоута (regression-safety на будущее).
  2. Громкий лог дропа. Подавление approved-по-draft фиксируй в retrieval-state (новое поле/счётчик — напр. n_suppressed_by_lower_trust + detail) ИЛИ в общий канал наблюдаемости, чтобы дроп был виден оператору. Не молчать.
  3. Убрать устаревшую D1-моно-рационализацию в комментах renderEditorConstraintBlock (memory.go:469-503): редактор теперь БИЛИНГВ (D30.1). Это коммент/рационализация, поведение CONFIRMED-only оставь как есть (флаг открытого вопроса «давать ли билингв-редактору src→dst» — в отчёт оркестратору, НЕ реализовывать). Снапшот: меняется выбор инъекции → bump memnorm/memory-версии → снапшот-fold + golden. Тест: сконструируй синт-чанк (draft-длиннее-approved) — покажи ДО (approved дропнут+тих) / ПОСЛЕ (approved инъектится ИЛИ дроп логируется). Зеркаль enforce-логику как в reseed-verify, но это КОД — верь Go.

T2 — Export-contract: нормализация каждого финала (слой 6) · находки L5-normalization-fused-to-flag-path, L5-stripcosmetic-duplicates-nfkc, L5-sanitizer-version-churn-*, L5-diacritics-class-lost

Проблема (CONFIRMED): нормализация вплавлена во FLAG-путь санитайзера (stripCosmetic) — один и тот же косметический дефект (U+3000-отступы, полноширина) чинится на флагнутом чанке, но уезжает СЫРЫМ на чистом (cmd/tmctl/render.go:31 пишет FinalText дословно). Плюс stripCosmetic руко-катает NFKC-fold, дублируя norm.NFKC из memnorm.go.

Сделать:

  1. Единый export-contract слой: одна детерминированная нормализация, применяемая к КАЖДОМУ финальному тексту перед экспортом (NFKC-fold через norm.NFKC + пробелы/CJK-пунктуация/U+3000-нормализация + опц. combining-диакритика→NFC, закрывает потерянный класс L5-diacritics-class-lost). Это слой D29.1a, который D-лог откладывал.
  2. Санитайзер → чистый ДЕТЕКТОР: он только ФЛАГует по-настоящему невосстановимое; нормализацию у него забирает export-contract. stripCosmetic перестаёт дублировать NFKC. Сократи churn-регексы (L5-sanitizer-version-churn) там, где их заменяет единый нормализатор.
  3. Взаимодействие с D35.4a strip-and-export: сам strip-механизм ВЕРНЫЙ (L5-strip-export-mechanism-sound) — не ломай его; но нормализация теперь общая, а не только на флаге. Снапшот/golden: меняется текст экспорта → снапшот-fold + golden обновить. ⚠ Не тронь легит-кейс L5-legit-cjk-gloss-stripped (гло́сса (羅漢拳) в скобках) — это открытый вопрос владельцу (в отчёт, НЕ решай сам: узкий whitelist или конвенция — за владельцем). Тесты: чистый чанк с U+3000/полуширина/диакритикой — ДО (уезжает сырым) / ПОСЛЕ (нормализован).

T3 — Сеамы расширяемости + слой-2 сеам (слои 7, 2) · находки L8-*, L4-adding-a-pair-is-a-rewrite-not-config

Проблема: расширения, которые проект вот-вот делает (Ф2-annotator/voice-роль, пары языков), падают на грязные сеамы. Сделать (все — чистые рефакторы, поведение zh→ru НЕ меняется):

  1. Реестр role→инъекция (chunkrun.go:134-140): map[role]injectionRenderer (или стратегия из конфига) вместо рукоправимого switch — чтобы новая роль = данные + один renderer, не правка switch.
  2. Target-aware readability-гейты (L8-readability-gates-target-blind): весь readability-набор (санитайзер + cheap-гейты) сейчас хардкодит target=ru и стреляет безусловно — любой не-ru target даст ложные флаги. Загейти за TargetLang (early no-op при target≠ru ИЛИ per-target реестр), зеркаля pair-aware coverage/classify. Прод zh→ru не меняется.
  3. Слой-2 СЕАМ — pair-keyed резолв промпта/правил (решение владельца D39: сеам сейчас, контент zh→ru): стадия резолвит промпт из pair-keyed слоя (src→tgt[×genre]), а не из фикс-пути yaml. Book.LangPair() (мёртвый код book.go:193) оживает как ось. Наполнить ТОЛЬКО zh→ru (сегодняшний translator.md/editor.md-контент переносится в zh→ru-пакет). Fail-loud на отсутствующую пару (не молчаливый дефолт в zh — закрывает латентный баг: ja-книга сейчас едет через «китайский паратаксис»-промпт). Язык промпта = свойство пакета (сегодня русский — но как свойство, не хардкод). Снапшот обязан fold-ить биндинг пара→промпт. Прод zh→ru рендерит тот же SHA (поведение-нейтрально) — проверь исполнением.
  4. De-смазка strip-and-export (L8-sanitizer-strip-smeared-5-files, L8-stripcosmetic-recomputed-twice): один resolver classification→(disposition, exportText), считающий strip ОДИН раз; runStage/escalation/resume потребляют resolved-текст, а не каждый ре-branch-ится на FlagSanitizerStripped.
  5. Снести мёртвое: сироты-промпты analyst.md/terminologist.md/дубль editor-mono.md (если реально не референсятся — проверь grep; если editor-mono живой фолбэк, wire его конфигом под snapshot/golden); мёртвый gatesEnabled() (pipeline.go:164, 0 вызовов, врёт); вынести foldModelWire() (snapshot.go:235-280, tripwire на 3-ю модель-ось уже стоит). Снапшот: T3 — поведение-нейтрально для zh→ru; докажи исполнением (тот же снапшот/golden на прод-конфиге). Пункт 3 (pair-резолв) — самый тонкий: снапшот должен fold-ить пару, но SHA промпта zh→ru не измениться.

T4 — Детерминированный per-run quality-report (слой 5, детерм. часть) · находка H5-no-in-loop-quality-signal

Проблема: владелец просил телеметрию качества с 1-го дня (п.25/п.31); research/18 §C1 #10 пометил авто-оценку «СЕЙЧАС», её молча спустили в Ф2. Сигналы УЖЕ считаются (cheap-гейты, regression-guard, glossary post-check, echo/CJK), но не агрегированы в читаемый per-run сигнал качества. Сделать (ЛЁГКО — переиспользуй существующее, НЕ строй судью): агрегируй уже-считаемые детерминированные сигналы в per-run quality-report (напр. tmctl report / расширение status): предл./нарратив-абзац (KPI претензии-1), диалог-тире-консистентность, CJK-утечка rate, glossary-консистентность (D10), число-дрейф (regression-guard), echo-rate. Per-chunk + агрегат по книге. Только наблюдаемость, НЕ гейт. Семантический span-судья (inversion/omission-бэкстоп претензии-2) — НЕ здесь: он ресёрч-зависим (форму даст трек B), в пак-2. Снапшот: read-only проекция (как status) → снапшот не трогает. Тест на живом store-срезе (можно синт).

Что НЕ делать (в отчёт оркестратору, не реализовывать)

  • Семантический inversion/omission-судья (пак-2, после трека B).
  • Наполнение не-zh→ru пар (слой 2 — только сеам сейчас).
  • Diff-based редактор / декаплинг edit-единицы / логическая нарезка — это трек B (ресёрч ПЕРЕД постройкой).
  • Решение по CJK-глоссам в скобках (L5-legit-cjk-gloss) — вопрос владельцу.
  • Пере-прогон книги — гейтится треком B.

Deliverable / отчёт оркестратору

Отчёт в ответе (НЕ в PROGRESS — туда пишет оркестратор): по каждой T — что сделано, file:line, ДО/ПОСЛЕ на реальном/синт-входе, какие снапшот-версии сдвинулись, какие golden обновлены и ПОЧЕМУ, результат build/vet/gofmt/test -race. Явный само-ревью исполнением (мандат): перечисли, что пинал реальным тестом, что прогнал руками. Открытые вопросы (билингв-редактор src→dst; CJK-глоссы; editor-mono судьба) — списком оркестратору. Не коммить. Оркестратор верифицирует исполнением (второй рубеж) и лендит.