textmachine/docs/archive/prompts/BACKEND_SESSION_PROMPT_REFACTOR.md

6.6 KiB
Raw Blame History

Промт: бэкенд-сессия — пакет №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.mdbackend/README.md (инварианты 17 — ломать нельзя, каждый закреплён тестами).
  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. Инварианты 17 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.