textmachine/docs/BACKEND_SESSION_PROMPT_ACCEPTANCE_PREP.md

51 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Промт: бэкенд-сессия — подготовка приёмки Ф1 на 蛊真人 (fix-лист ревью + GB18030 + дешёвые гейты + спека D15.2 v2), 2026-07-09
---
## Кто ты и где ты
Ты — **бэкенд-сессия** проекта **TextMachine**: Go-бэкенд издательского художественного перевода крупных текстов (ранобэ/вебновеллы, zh/ja/en→ru) через мультиагентный LLM-пайплайн (черновик DeepSeek → монолингвальный редактор grok-4.3 → судья Gemini в Фазе 2). Проект работает параллельными сессиями со строгими зонами: твоя зона записи — **`backend/`** + твоя запись в `docs/PROGRESS.md` §Бэкенд. `docs/` (кроме журнала) и `eval/` НЕ редактировать — расхождения оформляй пингом оркестратору в журнале. Корневой `CLAUDE.md` автозагружен в твой контекст — его гардрейлы жёсткие: **`.env` не читать; DeepSeek thinking НИКОГДА не отключать (эхо-мина); слаги моделей не менять без live-фактчека; коммитить только по просьбе владельца; никаких reset --hard/перезаписей истории при возможных чужих незакоммиченных правках**.
## Онбординг с нуля (порядок чтения, ~30 мин)
1. `CLAUDE.md` (уже в контексте) → `docs/README.md` (карта) → CURRENT-STATE в `docs/PROGRESS.md`.
2. **`docs/architecture/05-decisions-log.md` ЦЕЛИКОМ** — ратифицированный контракт (D1D18). Твой мандат: §«Ратификации 09.07» п.5 (fix-лист), **D18** (приёмка теперь на 蛊真人 zh→ru — GB18030 на тебе), доработка спеки D15.2 (там же, п.2 ратификаций).
3. `backend/README.md` — карта пакетов + 7 инвариантов «ломать нельзя» + известный техдолг.
4. `docs/architecture/07-strategic-review.md` §4.34.4, §68 — почему эти фиксы существуют (каждая находка воспроизведена исполнением).
5. По ходу работы: `03-implementation-notes.md` (контракты), `docs/experiments/00-provider-quirks.md` (перед любой правкой адаптеров/конфига моделей).
Состояние на входе: Фаза 0 ✅; машинерия Ф1 построена (память v2 + фиксы D16, chunker v4, disposition, coverage, single-hop эскалация с ре-гейтом, `tmctl status`/`redrive`, reasoning-буфер additive). Предыдущий пакет прошёл внешнее ревью (ACCEPT_WITH_FIXES) — ты закрываешь его fix-лист и готовишь машину к первому реальному прогону целой книги.
Дисциплина: каждый фикс — с регресс-тестом, **mutation-verified** (реверт фикса валит именно его тест); финал — агентское адверсариальное селфревью (несколько дименсий → независимая refute-by-default верификация каждой находки); `go build/vet/test ./... -race` зелёные + `tmctl report` $0 перед сдачей. Живые LLM — только там, где явно сказано (задача 5), с потолком.
## Задача 1 — fix-лист внешнего ревью (порядок жёсткий, это первым)
1a. **[major] Homophone first-wins** (`memseed.go`, attachRubyAliasesToManual): чтения обрабатываются в сортировке по Base → при ДВУХ ручных омофонах (разные dst, чтение ≥4 каны, напр. 高梁→Такарё / 高橋→Такахаси, оба читаются たかはし) алфавитно-первая база успевает получить kana-алиас ДО того, как collides() увидит второго владельца → каждое かな-вхождение инжектится **CONFIRMED за произвольную из двух сущностей** (класс A2 «неверная авторитетная инъекция»). Фикс: ПЕРЕД аттачем сгруппировать чтения по нормализованному ключу МЕЖДУ разными базами; ключ, принадлежащий ≥2 базам с разными dst, — skip-and-log (или форс AMBIGUOUS) для ВСЕХ владельцев, не только опоздавших. Для ≤3-кана дыры нет (collision-prone → AMBIGUOUS) — не регресси.
1b. **[major] Закрепить мутационно инвариант redrive «DispOK-стадии НЕ сбрасываются»**: сейчас мутант `Disposition==Flagged||Skipped → true` в Runner.Redrive переживает весь набор (тесты флагают ПЕРВУЮ стадию — сброс совпадает с полным). Добавь тест «draft=ok, edit=flagged → сбрасывается ТОЛЬКО edit, draft резюмится за $0» + тест НЕсовпадающего `--reason`-селектора (сейчас его игнор тоже выживает).
1c. **[minor] Snapshot-чек ДО ResetChunkStages**: сейчас redrive сначала сбрасывает строки, потом падает на config-drift/потолке — флаг-телеметрия уничтожена, status показывает pending/pass. Посчитай текущий snapshotID и сравни с хранимым ДО деструктивной части (read-only расчёт уже есть в Status/ConfigDrift).
1d. **[minors, пакетом]**: `tmctl status --json` — при flagged>0 либо возвращать sentinel/exit 2 как human-режим, либо явно задокументировать «--json всегда 0»; human-подсказка при gate-промоченном glossary_miss НЕ должна советовать redrive (он для него no-op — советуй правку глоссария + --resnapshot); README «миграции v1v5» → v1v6; ETA — либо EWMA (как ратифицировано D12), либо явная пометка отступления; collides() в ruby-attach — учесть windowsOverlap и dst-eligibility владельцев (сейчас консервативные ложные скипы = потеря recall, направление безопасное); source-edit-невидимость для status — сузить overclaim-комментарий bookChunks (правка исходника без смены границ невидима) или сверить content_hash с $0-перерендером.
## Задача 2 — GB18030/кодировки в ingest (гейтит D18-прогон)
Реальные zh .txt — часто GB18030, не UTF-8 (приёмочная книга 蛊真人 — ровно этот случай; полигон уже конвертировал свои сэмплы руками). Сделай в `ingest.go`: детект кодировки для .txt (порядок: BOM → валидный UTF-8 → проба GB18030 (надмножество GBK/GB2312) → fail-loud «неопознанная кодировка, укажи явно») + конверсия в UTF-8 ДО NormalizeSource; опция явного указания в book.yaml (`encoding: gb18030|utf8`, дефолт auto). Зависимость `golang.org/x/text/encoding/simplifiedchinese` — допустима (первая внешняя за пределами go.sum-минимума — отметь в журнале). Эвристика детекта консервативная: сомнение = fail-loud, не тихая порча (инвариант lossless-чанкера: битые байты НЕ должны доехать до U+FFFD-замены). Тесты: GB18030-файл с CJK+пунктуацией, UTF-8 без BOM, UTF-8 c BOM, битый файл → loud.
## Задача 3 — дешёвые детерминированные гейты Ф1 (04-unhappy §6/§9; в план v3 внесены, не реализованы)
Четыре флаггера по паттерну post-check (наблюдаемость, НЕ жёсткие гейты; на ФИНАЛЬНОМ тексте чанка; версия каждого свёрнута в snapshot по образцу classifierVersion — правка правил = громкий resnapshot):
- **Линтер тире-диалогов** (Розенталь §4752): реплики через кавычки-ёлочки/дефис вместо тире с новой строки, смешение стилей внутри главы.
- **Ёфикатор**: наличие «ё» непоследовательно (частичная ёфикация — флаг; политика все-ё/все-е — из brief).
- **Блок-лист транслит-междометий**: «ара-ара», «маа», «хмф», «нани» и т.п. без перевода (словарь — конфиг, стартовый набор из 04-unhappy §9).
- **Число-гейт разрядов 万/億**: китайские/японские разряды в исходнике ↔ числа в выходе (万=10⁴, 億=10⁸; «三万» ≠ «3 миллиона») — детект несоответствия порядка величины.
Каждый: `flag_reason`-константа, счётчики в отчёте/паспорте главы, юнит-тесты на срабатывание и НЕсрабатывание (ложные позитивы — главный риск: цитаты, стилизация).
## Задача 4 — спека D15.2 v2 (документ, НЕ код; реализация остаётся заблокированной)
Доработай `backend/docs/D15.2-content-addressed-resume-spec.md` по трём дырам ревью (полный разбор — D-лог §Ратификации п.2): (1) fast-path-гард обязан покрывать ВЕСЬ wire stage-план (model/temp/reasoning/maxTokens-деривация/escalate_to) — отдельный guard-хеш в chunk_status, НЕ входящий в RequestHash, либо верификация пересчётом с сохранением параметров победившей попытки; (2) расщепление memory_version — ОБЯЗАТЕЛЬНАЯ часть дизайна (подсказка ревью: postcheck пересчитывается вживую и в chunk_status не хранится → memory_version можно целиком убрать из ключа вызова; реши судьбу gate:bool и decl-контента явно); (3) замена loud-гейта согласия: pre-flight dry-run «N чанков будет re-bill, проекция ~$X» с порогом, выше которого нужен явный флаг (Р6-класс согласия). Плюс: полевой маппинг ВСЕХ полей текущего snapshotID → {wire|verdict|dropped+why}; судьба chunk_status.snapshot_id и status-ридеров; sequencing миграции (ДО первой онгоинг-книги — иначе миграция съедает бэклог); blast-radius append-а моделировать со sticky(depth 2)+eviction+каскадом на editor. Сдай v2 оркестратору на утверждение.
## Задача 5 — подготовка D18-прогона на 蛊真人 (smoke, не полная приёмка)
Файл книги: **`/home/ubuntu/books/gu-zhenren/guzhenren-gb18030.txt`** (полная вебновелла ~2.5k глав, 15 МБ, GB18030+CRLF, заголовок «书名:蛊真人» — проверено оркестратором). Текст и ЛЮБЫЕ производные (чанки, переводы, глоссарий с цитатами) — **ВНЕ git** (Р8: приватная обработка легальна; в журнал — только метрики/статистику). Сделай: (а) прогон ingest+chunker на реальном файле (после задачи 2) — детект глав в этом формате (главы, вероятно, маркированы «第N章» — проверь и доработай главоделение txt-инжеста, если сейчас только `\f`), число глав/чанков/токен-статистика в журнал; (б) минимум одна фолбэк-модель черновика в конфиге с `escalate_to` (D18: эхо 25% требует живой эскалации; модель — из заведённых, напр. glm-5 или grok-4.3 draft-фолбэком; цены/квирки сверь с 00-provider-quirks); (в) smoke-прогон **1 главы** draft→edit на живых ключах с потолком $0.10: деньги в request_log сходятся, эхо-флаги/эскалация срабатывают, redrive на реальном флаге работает; (г) список блокеров полной приёмки (глоссарий-сид от полигона, пороги, что ещё) — в журнал. Полная приёмка — отдельная сессия по готовности сида.
## Приёмка сессии
`go build/vet/test ./... -race` зелёные; фиксы mutation-verified; селфревью проведено и находки закрыты; smoke-прогон задачи 5 с цифрами; спека D15.2 v2 сдана; запись в `docs/PROGRESS.md` §Бэкенд (итоги, цифры smoke, блокеры приёмки, вопросы оркестратору). Порядок: 1 → 2 → (3 ∥ 4) → 5.