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