textmachine/docs/BACKEND_TRACKA_PACK15_SESSION_PROMPT.md

12 KiB
Raw Blame History

Промт: сессия-БЭКЕНД — Трек A, пак-1.5 (экспорт-поверхность + глосс-whitelist + пример), 2026-07-16

Малый хирургический пак ПОСЛЕ приёмки пака-1 (D39.2, d3f6b34). Три задачи — все НЕ гейтятся эмпирикой exp15 и закрывают открытые вопросы D39.2 с решениями владельца от 16.07. Крупные слои (чанкер B1, кнобы когезии, дифф-apply) НЕ строить — они гейтятся результатами exp15 (research/19 §D). Смысл окна: все снапшот-сдвиги этого пака ложатся В ТОТ ЖЕ единый --resnapshot, который и так предстоит (D38.5+пак-1) — сейчас они бесплатны, после resnapshot стоили бы вторую переоплату книги.

Зона/дисциплина (как в паке-1)

backend/ только; не коммитить; .env не читать; мандат самопроверки ИСПОЛНЕНИЕМ (build/vet/gofmt/ test -race, каждый фикс запинен firing+non-firing тестом, ДО/ПОСЛЕ на сконструированном входе); golden = инвариант №8 — re-capture только с обоснованием «что и почему сдвинулось» (ратифицировано D39.2-open + решения владельца 16.07); git status перед работой — в дереве возможны параллельные сессии.

Онбординг

CLAUDE.md → D-лог D39.2 (что залендено паком-1 + открытые вопросы) → 09-target-architecture.md §2 (слой 6) → свой же код пака-1 (sanitizer.go exportNormalize/detectCJKLeak/stripCosmetic, chunkrun.go FinalText, cmd/tmctl).

T1 — Read-only экспорт-поверхность для полигона (закрывает D39.2-open №1; НУЖНО ДО пере-прогона)

Проблема: exportNormalize живёт на бэкенд-поверхностях (translate stdout / report), а полигонная экстракция (records.json/exp12_extract-класс) читает СЫРЫЕ чекпоинты из store → полигон и читатель-сэмплы увидят НЕнормализованный текст. Решение оркестратора: НЕ Python-зеркало нормализации (анти-паттерн зеркального дрейфа), а бэкенд-поверхность. Сделать: read-only команда tmctl export (проекция класса status/report: OpenReadOnly, $0, без снапшот-влияния): для каждого чанка книги эмитит финальный экспорт-текст РОВНО по прод-семантике (chunk_status.final_hash → checkpoint.response_text → [для sanitizer_stripped — это уже стрипнутый derived-чекпоинт] → exportNormalize) + метаданные (chapter/chunk/disposition/flag/ snapshot_id) в стабильном JSON (+опц. plaintext). Флагнутые-непоправимые — пустой текст + причина (как FinalText-семантика). Это и есть отложенный tmctl export из D15.2-этап-Б в минимальной форме — НЕ строй annotations/override-часть, только чтение. Тест: export == translate-FinalText байт-в-байт на golden-фикстуре (fresh и resume).

T2 — CJK-глосс-whitelist (решение владельца 16.07: глоссы СОХРАНЯТЬ)

Сейчас: stripCosmetic стрипает намеренный глосс «Кулак Ло Ханя (羅漢拳)» и пустит скобку; detectCJKLeak флагует такой Han-ран. Сделать: узкий whitelist — сбалансированный Han-ран целиком внутри скобок, непосредственно после кириллического/латинского слова-головы (паттерн «терм (漢字)») — НЕ утечка: detectCJKLeak не флагует, stripCosmetic/exportNormalize сохраняют байты глосса (вкл. НЕ-фолд его полноширинных символов, если они часть глосса). Голый Han вне этого паттерна — по-прежнему утечка (прод-класс cjk_leak цел). Краевые: вложенные скобки, глосс с пробелами/ пиньинем, глосс в начале строки без головы (→ утечка), два глосса подряд. sanitizerVersion v5→v6 + golden re-capture (обоснование: решение владельца). Тесты: глосс сохранён (strip и normalize), standalone-Han флагуется, пустая скобка больше не рождается.

T3 — example/book.yaml: вернуть runnable

После pair-сеама ja-пример fail-loud'ится через zh-ru-keyed pipeline-c1 (сеам работает как задуман). Сделать: переоформить пример на zh→ru (реальный прод-контент пары уже есть) — пример снова проходит end-to-end; рядом закомментированный ja-блок с пояснением «fail-loud до появления ja-ru пакета» (демонстрация сеама, не заглушка). ja-ru пакет НЕ сочинять (контент пары = данные под реальную книгу, инвариант общности §0.1).

T4 — ФИКС-ЛИСТ адверсариала (долг D39.2 ПОГАШЕН 17.07: 8 осей, оба пака; 0 CRITICAL / 1 HIGH / 6 MEDIUM)

T1T3 исполнены и верифицированы оркестратором (не лендились — ждут этих фиксов, чтобы санитайзер-бамп и golden re-capture легли ОДНИМ лендингом). Обязательные (F1F6) + дешёвые LOW (F7F9):

F1 (HIGH, санитайзер — обход гейта, воспроизведён исполнением). 5 из 6 классов детектят по СЫРОМУ тексту (fold делает только detectCJKLeak), а exportNormalize шипит СВЁРНУТЫЕ байты → полноширинная вариация сигнатуры обходит детектор, и нормализация ИЗГОТАВЛИВАЕТ ASCII-форму дефекта в экспорте: «Вот отредактированный перевод:»(U+FF1A) / «### Глава 5» / trailing «Примечание:…» / fullwidth-латиница с U+3000 — все total=0, а в экспорте — ASCII-дефект (ASCII-близнецы при этом флагуются). Регресс vs v4 (тот фолдил весь U+FF00FFEF блок и re-check ловил). Фикс: fold-first — t := recoverableFold(text) в начале sanitizeOutput; ВСЕ шесть классов + глосс-маскирование работают по свёрнутому тексту (детекция по folded; strip/export-пути сохраняют оригинальные байты; глосс-спаны пере-анкерить согласованно). + инвариант-тест гейта: для чистого вердикта sanitizeOutput(exportNormalize(x)).total()==0 по всему тест-корпусу. Версия: v6 НЕ лендился — влить fold-first в ТОТ ЖЕ бамп v5→v6, один golden re-capture. F2 (MED, глосс — unbounded латиница; найден независимо осями 4 и 8). isValidGloss ограничивает только CJK: «Кулак (拳 the quick brown fox…) взлетел.» = total 0, английская фраза уезжает чистой (без 拳 — substantive drop). Фикс: ограничить не-CJK контент — кап полной внутренней длины (~24 руны) + пиньинь-правдоподобные латин-токены (lowercase, ≤~7 рун токен, ≤~maxGlossCJKRunes+2 токенов). Запинить тестом «(拳 the iron fist that breaks the sky)» → НЕ глосс. F3 (MED, export — gate-drift). Export() читает PostcheckGate из ТЕКУЩЕГО конфига: флип гейта между прогоном и экспортом тихо шипит удержанный текст (или фабрикует glossary_miss). Фикс: сравнить SnapshotID строк с текущей проекцией (currentSnapshotProjected, как status) → fail-loud ИЛИ top-level config_drift поле + WARN; docstring-caveat расширить на gate-flip вариант. F4 (MED, export — нет manifest-сверки). Частичная книга экспортится молча как полная (класс D37-скью для полигона); пустая книга даёт "chunks": null; ghost-строки удалённой главы экспортятся. Фикс: Chunks = non-nil []; join с $0-манифестом (bookChunks) → pending/missing явными строками или total/missing-счётчиками; строки вне манифеста — drop или громкая метка. F5 (MED, quality — популяция KPI). QualityReport считает gate-withheld чанки в TextChunks/KPI (их экспорт = ""), расходясь с export. Фикс: зеркалить gateOn re-derivation (данные уже читаются) — исключать из TextChunks/KPI. F6 (MED, quality — метка метрики). cjk_leak_rate считает ВСЕ sanitizer_stripped, вкл. markdown-only (ложные 30% «CJK-утечек»). Фикс: сплит по классу strip (из detail) на markdown_strip/cjk_leak ИЛИ переименовать в cosmetic_strip_rate. F7 (LOW, 1 строка). resume.go stripped-ветка: sr.Model = cp.ModelActual (зеркало ok-ветки) — иначе resumed-строка телеметрии атрибутируется конфиг-слагу вместо фактической модели. F8 (LOW, 1 строка). example/book.yaml: transcription: pinyinpalladius (ru-target конвенция; самопротиворечие примера). F9 (LOW). Export fail-loud parity: DispOK-строка с ПУСТЫМ final_hash → тоже громко (сейчас уже комментарий обещает resume-дисциплину — довести до неё).

В ledger/очередь (НЕ этот пак): Retries не в снапшоте (pre-existing, дизайн); pair-fail-loud после store.Open + report/export не требуют промптов (дизайн-вопрос); trustGateEvent только первый отказ; WarnContext на каждом resume; quality 0.00 при all-dialogue; пунктуационные «огрызки» в НЕ-whitelisted скобках; golden-ячейка глосса и escalated+stripped (желательно добавить тестами при F1/F2).

Deliverable

Отчёт в ответе (не в PROGRESS): per-T — file:line, ДО/ПОСЛЕ, какие снапшот-версии/golden сдвинулись и почему, результаты build/vet/gofmt/test -race, само-ревью исполнением. Открытые вопросы — списком. Не коммитить — лендит оркестратор.