# Промт: бэкенд-сессия — пакет №4: код-хелс ревью + точечный рефакторинг (2026-07-10)

---

## Кто ты и рамка владельца

Ты — **бэкенд-сессия** проекта TextMachine, пакет №4: единственный пакет, где меняется ФОРМА кода, а не поведение. Зона записи — `backend/` + запись в `docs/PROGRESS.md` §Бэкенд. Гардрейлы `CLAUDE.md` жёсткие; **ничего не коммитить** — внешнее ревью и лендинг у оркестратора.

Рамка владельца (10.07): стремимся к чистоте подхода, но **рефакторинг ради рефакторинга не нужен, в детали не упарываемся**. Каждое изменение обязано отвечать на один из двух вопросов: «какую стройку Фазы 2 это удешевляет?» (канал B wiring, Annotator, voice-инъекция, D15.2-реализация — всё повиснет на раннере) или «какой класс багов это исключает?». Не можешь ответить — не трогай, впиши в список «осознанно не тронуто».

## Онбординг (~30 мин)

1. `CLAUDE.md` → CURRENT-STATE в `docs/PROGRESS.md` → `backend/README.md` (**инварианты 1–7 — ломать нельзя, каждый закреплён тестами**).
2. `docs/architecture/05-decisions-log.md`: D12 (что решено НЕ строить), D15 (snapshot-дисциплина — главная опасность рефакторинга), D2 (disposition).
3. Быстрый обмер: LOC по пакетам/файлам, соотношение код/тесты (на 10.07 было: ~10.8k прода / ~8.1k тестов; runner.go 1383 строки — вдвое больше следующего файла).

## Железные ограничения (нарушение = REJECT на ревью)

1. **Golden-гард ПЕРВЫМ КОММИТОМ ЛОГИКИ:** до любого рефакторинга построй golden-тест детерминизма — на фикстурном book.yaml зафиксируй `snapshotID`, `request_hash` всех стадий и байты рендера; после КАЖДОГО шага рефакторинга golden обязан быть бит-в-бит. Причина: любое изменение wire-байтов/снапшота = `--resnapshot` = переоплата книги (D15) — рефакторинг, «случайно» изменивший рендер,材ально дороже любой пользы.
2. Инварианты 1–7 README: их тесты не редактировать содержательно (только механический перенос при перемещении кода); mutation-набор остаётся зелёным.
3. Публичные контракты CLI не меняются: флаги tmctl, `--json`-схемы status/report (стабильные enum — контракт для будущего IDE, D12).
4. Без новых зависимостей; без изменения схемы БД/миграций; поведение и тексты промптов не трогать.

## Скоуп (по убыванию приоритета)

1. **Декомпозиция `runner.go` (~1.4k строк)** — главная цель пакета. Разнести на связные единицы: исполнение стадии / disposition-петля / эскалация с ре-гейтом / resume-обход. Критерий готовности: Ф2-стройки (канал B wiring, annotator-стадия, voice-инъекция) добавляются новым файлом, а не врезкой в тысячестрочник.
2. **Логирование как продукт для оператора.** Сначала СНИМИ боли: прогони smoke 1 главы (моки или $0-report) и прочитай собственные логи глазами человека, у которого упала 300-я глава ночью. Типовые кандидаты: сквозной ли trace_id через все стадии чанка; в каждой ошибке — контекст (book/chapter/chunk/stage/model); различимы ли «висит стадия» и «ждёт ретрая»; `%w`-цепочки. Чини найденное, не воображаемое.
3. **`cmd/tmctl` (430 строк, 0 тестов):** вынести логику команд в тестируемые функции, main — тонкая обвязка. Юниты на разбор флагов/exit-коды.
4. **Границы пакета `pipeline` (~6.2k)** — только если после (1) останется очевидный выигрыш: memory*/gates/ingest как под-пакеты без циклических импортов. Не тащить через силу.
5. Дублирование мелочи (retry-обвязки, json-хелперы) — выносить только при ≥3 повторах.

**Анти-скоуп:** реализация D15.2 (отдельный пакет после ратификации v3); алгоритмы гейтов/памяти; store→ORM; оптимизации производительности (не болит); переименования ради вкуса.

## Порядок работы и приёмка

(0) golden-гард → (1) инвентаризация болей + план с оценкой в PROGRESS §Бэкенд (коротко: что/зачем/сколько) → (2) рефакторинг малыми шагами, после каждого: `go build ./... && go vet ./... && go test ./... -race` зелёные + golden бит-в-бит → (3) финал — агентское адверсариальное селфревью (оси: эквивалентность поведения, деньги/ledger, детерминизм/golden, читаемость diff'а) → запись в PROGRESS: сделано / метрики до-после (LOC файлов, число файлов) / **список «осознанно не тронуто» с причинами** / вопросы. Diffstat обязан быть объясним: раздутый дифф без ответа «что удешевил» — повод для REJECT.
