textmachine/backend/docs/SEAM_PACK_PLAN.md

70 lines
8.1 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.

# Записка-план: бэкенд-пак «входная дверь шва» (движковая половина 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` тоже платный путь, уходит ПИНГОМ, а не тихой правкой.