textmachine/backend/docs/SEAM_PACK_PLAN.md

8.1 KiB
Raw Blame History

Записка-план: бэкенд-пак «входная дверь шва» (движковая половина D39.156)

Сессия бэкенда, 24.08.2026. Промт — docs/BACKEND_SEAM_PACK_SESSION_PROMPT.md. Доказательная база и посадки — backend/docs/SEAM_PACK_FINDINGS.md (пишется по ходу).

0. Точка отсчёта, снятая ИСПОЛНЕНИЕМ (до правок)

факт команда
go build ./... · go vet ./... · go test ./... -count=1 — зелены из backend/
стенд-батарея с переопределениями: красен ровно TestMinerFullBookParity, причина — отсутствующий eval/exp16/data/jieba_dict_general_zh.txt TM_MINER_PARITY=1 TM_CHECKER_LABELS=1 TM_CHECKER_LABELS_DIR=<repo>/books/gu-zhenren/labels TM_MINER_PARITY_RECORDS=<repo>/books/gu-zhenren/rerun/records.json TM_MINER_PARITY_SEED=<repo>/books/gu-zhenren/guzhenren-seed-v2.yaml go test ./... -count=1
рабочее дерево backend/ чистое; незакоммиченная чужая работа — только в eval/ git status --porcelain

1. Комплектность против §4 промта (сверка МЕХАНИЧЕСКАЯ)

§ заказ форма, которую я выбрал где
4.1 дефолты пустых ключей mined_delta/mined_rejects по конвенции каталога книги <dir книги>/<book_id>.mined-delta.yaml и <book_id>.mined-rejects.yaml; дефолт ТОЛЬКО на пустом ключе; отсутствующий дефолтный файл = «решений нет» internal/config/book.go, internal/pipeline/mining.go
4.2 $0-глагол приёма правок банка tmctl bank-apply --config <book.yaml> --decisions <decisions.json> [--dry-run] internal/membank/decisions.go (чистая логика), internal/pipeline/bankdecisions.go (обвязка), cmd/tmctl/bankapply.go
4.3 путь к файлу ключей аргументом --keys-file <path> ТОЛЬКО у translate; грузится ПЕРВЫМ; названный и нечитаемый = отказ конфигом (10) cmd/tmctl/invocation.go, cmd/tmctl/dotenv.go, cmd/tmctl/main.go
4.4 строгость загрузчика сида + тест на unknown-key strict-декодер схемы в internal/seed (seed.DecodeFile), им же читается reject-файл internal/seed/decode.go, internal/membank/memseed.go, internal/pipeline/mining.go
4.5 путь артефакта и версия документа в status --json status_version + аддитивный объект artifacts{project_db,bank_export} (абсолютные пути) internal/pipeline/status.go
4.6 дефолт стендового корпуса в трёх тест-хелперах маркерный резолв корня репозитория (repoFile), корпус — <repo>/books internal/miner/miner_parity_test.go, internal/checks/labelharness_test.go, internal/membank/labelharness_test.go
4.7 платных вызовов ноль глагол $0 по построению; --keys-file — юнит-тесты + фейковые ключи

2. Порядок работ

  1. §4.6 (дешёвая, независимая) → стенд-батарея зеленеет без переопределений.
  2. §4.4 строгость схемы — она нужна ДО глагола: глагол читает и пишет те же файлы, и нестрогий читатель обесценил бы строгого писателя.
  3. §4.1 дефолты путей — без них глаголу некуда писать на книге, которая ключей не объявляла.
  4. §4.2 глагол (основной кусок).
  5. §4.3 --keys-file.
  6. §4.5 версия и пути артефактов в status --json.
  7. Адверсариальные посадки, живая проба на копии стендовой книги, отчёт.

3. Развилки, которые я вижу, и как решаю

(а) Имена дефолтных файлов — каталог книги или сайдкар при базе? Все банк-сайдкары движка висят на project_db (.bank.json, .auto-bank.yaml, .mined-signature.yaml). Беру всё-таки КАТАЛОГ КНИГИ с префиксом book_id, ровно как project_db по умолчанию: project_db может указывать КУДА УГОДНО (в т.ч. за пределы каталога книги), а решения пользователя — это его данные, они обязаны ехать с книгой в бэкапе и экспорте каталога. Префикс book_id повторяет механизм <book_id>.db и не даёт двум книгам в одном каталоге столкнуться.

(б) Форма проекции до мутации. --dry-run, прецедент — redrive --dry-run и строка 124(в) (resnapshot --dry-run поименован там дословно). Проекция получается БЕЗ мутации: с флагом не пишется ни один байт.

(в) Код выхода для отвергнутого набора решений. Занятый лок — ровно 12 (заказано). Для отвергнутых решений НЕ завожу новый номер полосы: беру 10 (config_invalid). Довод по оси, которая у D39.134 несущая, — КТО ДЕЙСТВУЕТ: при 12 действует время («подожди и повтори»), при 10 действует человек. Набор решений, конфликтующий с подписанной строкой сида, чинит человек, а не время. Отчёт при этом печатается в stdout ВСЕГДА и называет каждый отказ поимённо (прецедент: status --json печатает отчёт и выходит 2).

(г) Словарь входа глагола. Ровно словарь УЖЕ публикуемой поверхности банк-экспорта (id/src/dst/kind/sense/since_chapter/until_chapter/aliases) плюс note. Поля, которого движок не публикует, во входе нет — в частности gender (строка 210: ось мертва в производителях); строить «на вырост» запрещено §4.3.

(д) Канонизация файла. Глагол — единственный писатель своей схемы, поэтому при изменении файл пере-рендерится целиком из разобранной схемы (комментарии оператора не переживают первую правку). Это ВИДНО до мутации: проекция несёт canonical_rewrite. Порядок термов сохраняется (замена — на месте, добавление — в хвост), чтобы правка не выглядела как переписывание всего файла.

(е) Что я НЕ делаю и почему. Не проектирую «глубину до черновика» (§4.2, прямое слово владельца): правка едет через mined_delta до РЕДАКТОРСКОЙ волны, и ответ глагола говорит это явным полем depth: edit_wave. Не трогаю redrive флагом ключей (промт: «флаг только у translate») — наблюдение о том, что redrive тоже платный путь, уходит ПИНГОМ, а не тихой правкой.