# Промт: БЭКЕНД-ПАК «ВХОДНАЯ ДВЕРЬ ШВА» (движковая половина D39.156) > Выдан оркестратором №19, 23.08.2026. Активный промт зоны бэкенда — один. Параллельно законен > только `platform/docs/archive/PLATFORM_P8_REVIEW_SESSION_PROMPT_2026-08-22.md (ОТРАБОТАН 27.08)` (read-only ревью платформы, пересечений нет). ## 1. Какая проблема и что решит твой результат Продукт — издательский перевод больших книг мультиагентным пайплайном. Его центральная ценность — **банк памяти**: сквозной глоссарий имён и терминов, который держит консистентность на всю книгу. Пользователь через веб должен уметь **поправить или добавить термин**, после чего прогон продолжается с учётом правки. Сегодня этого нет и построить нельзя: движок принимает снаружи только «запустись с флагами», а данные берёт из файлов, которые правит человек в редакторе. Положить правку пользователя некуда — писателя этих файлов не существует ни на одной стороне. Разбор шва (D39.156) показал, что это не дефект одного места, а **отсутствующая половина контракта**: наружу из движка идёт богатый поток — журнал событий, полоса кодов выхода, версионированные артефакты, — а внутрь не идёт ничего. Поэтому любой внешний участник вынужден имперсонировать человека-оператора с текстовым редактором. **Твой пак строит входную дверь.** После него платформа сможет доставлять решения пользователя в движок, не зная формата его файлов; развилка владения `book.yaml`, державшая тему, уже закрыта ратификацией — не выбором из вариантов, а тем, что объявлять ключи и создавать файлы заранее больше не нужно никому. Ещё один твой результат — **снятие блокера живого прогона**: на SaaS-деплое движок сегодня не получает провайдерских ключей вовсе, и это не гипотеза (строка бэклога 211). ## 2. Зона записи и git Пишешь ТОЛЬКО в `backend/`. Читать можешь всё; `platform/`, `frontend/`, `eval/`, `docs/` — чужие зоны, правки в них запрещены. **Единственное исключение:** пинги и итог сессии — в `docs/PROGRESS.md`, секция «Бэкенд» (своего журнала у зоны нет). Больше в `docs/` не пишешь ничего. **Ты не коммитишь** — дерево готовишь и передаёшь на лендинг оркестратору (канон git-норм — `CLAUDE.md`). `git add -A`, `git add .`, `git commit -a` запрещены в любом случае. В дереве живёт незакоммиченная работа параллельных сессий — не трогай её и не «прибирай». ## 3. Карта чтения (пять позиций, больше не нужно) 1. **`docs/architecture/17-seam-inbound-law.md`** — ратифицированный закон входной двери. Это твой контракт: семь пунктов, каждый с прецедентом. Читать ЦЕЛИКОМ и до кода. 2. **`docs/architecture/05-decisions-log.md`, нота D39.156** — почему закон такой и что он закрыл. 3. **`docs/PROGRESS.md` — строки БЭКЛОГА 199, 211, 212, 213** (предметные хвосты пака). ⚠ **Это ID строк ТАБЛИЦЫ, а не номера строк файла.** Брать грепом `^| 199 |`, `^| 211 |` и так далее. Буквальный `sed -n '199p'` даст совсем другой текст — правдоподобный, но чужой, и заказ ты прочитаешь неверно. ⚠ В теле строки 199 лежат ТРИ мины формата, которые обязана знать сессия, пишущая писателя этих файлов: явный `status: approved` · терм без `dst` · кап реверс-секции. Прочитать до кода. 4. **`docs/experiments/00-provider-quirks.md`** — обязательное пре-чтение перед любым касанием вызовов провайдеров (тебя касается через `--keys-file`). 5. **`docs/architecture/12-go-style-notes.md`** — норматив общности: Go-логика НЕ ветвится по паре и книге. Ревью-вопрос по умолчанию: «заработает ли на паре, которой в репо ещё НЕТ, без правки Go?» **Код первичен.** Все `file:line` ниже — отправные точки, а не истина: открывай и проверяй сам (дисклеймер о происхождении доков — шапка `CLAUDE.md`, здесь не повторяется). ## 3а. Что в секции «Бэкенд» — НЕ твой заказ (пропуск подписывается пропуском) В `docs/PROGRESS.md`, секции «Бэкенд», висят два пинга твоей зоне. **В пак НЕ входят ровно эти их предметы (испр. 24.08 — прежний абсолют «ни один из них не входит» был шире правды: отдельные предметы пинга №20 пак задевает законно, см. §4.6):** пинг про потерю инъекции банка на эскалации в Gemini (строки 208/210, родня 193) и пинг про разошедшиеся с деревом `backend/README.md` и `components.puml`. Не трогай их и не «допиливай попутно» — это скоуп-крип, и ответ полигону по Gemini даст отдельный промт. Если по ходу работы увидишь, что твой пак ломает или чинит что-то из них, — пинг, а не тихая правка. ## 4. Состав пака и разметка свободы ### 4.1 Конвенционные дефолты двух путей — ДЕЛАЙ РОВНО ТАК Пустые КЛЮЧИ КОНФИГА `mined_delta` / `mined_rejects` движок доставляет сам, по конвенции каталога книги (речь о незаполненном ключе, а не о создании пустых файлов). **Имена дефолтных файлов решаешь сам** — важно, что конвенция одна и живёт в движке. Механизм уже существует для базы проекта — повторить его: `backend/internal/config/book.go:163-167`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")`. Прецедент опционального конвенционного YAML-входа тоже есть: `backend/internal/pipeline/mining.go:644`=`.auto-bank.yaml`. **Жёстко:** дефолт срабатывает ТОЛЬКО на пустом ключе. Гард для ЯВНО объявленного пути не ослабляется ни на йоту — объявленный и нечитаемый файл остаётся ошибкой конфига. Отсутствующий дефолтный файл = «решений нет», как отсутствующая база до первого прогона. ### 4.2 Глагол приёма правок банка — РЕШИ САМ форму, ДЕЛАЙ РОВНО ТАК семантику Новый $0-глагол В `tmctl`. Прецедент глаголов без прогона есть (`migrate`, `backup`, `seed-lint`). **Имя, разбиение на подкоманды, схему входа и формат ответа решаешь ты** и аргументируешь в отчёте. Ориентир: вход — решения в словаре уже публикуемой движком поверхности (стабильные id банк-экспорта для правки существующих, полный кортеж для добавления); выход — JSON: что принято, что отвергнуто и почему. **Семантика — ровно так, отступление = пинг:** - **Повторный вызов тем же решением — байтовый no-op, exit 0, отчёт «уже применено».** Двойное возобновление и ретраи воркера существуют; неидемпотентный глагол расщепит состояние. - **Отзыв решения — это ЗАМЕНА, а не возврат в нерешённое.** «Вернуть в нерешённое» продукту не нужно при действующей модели подписи; если увидишь основание против — пинг, не решение. - **Частичный отказ запрещён: всё-или-ничего.** Приняли девять из десяти — файл не пишется, отчёт называет каждый отказ поимённо. - **Два решения по одному терму в одном вызове — отказ всего вызова как ill-formed.** - **Конфликт с подписанной строкой сида — отказ С ИМЕНЕМ**, молча принять нельзя: это заминирует следующий прогон. - **Проекция ДО мутации, получаемая БЕЗ мутации.** Закон говорит «почём до ПОДТВЕРЖДЕНИЯ», прецеденты — смета с порогом согласия и `--dry-run`. Формулировку «напечатал и тем же вызовом записал» считать НЕисполнением. Форму (режим, флаг, отдельная подкоманда) **решаешь сам и аргументируешь**. - **Занятый лок — отказ РОВНО классом 12**, не 10. Полоса различает их не косметически: 10 значит «деплой сломан, чинит человек, книги ждут», 12 значит «ничего не произошло, подожди и повтори» (в твоей зоне тот же класс уже назван: `backend/cmd/tmctl/main.go:68`=`another tmctl owns this project right now`; платформенная сторона — `platform/internal/ingest/exit.go:58`=`ExitProjectLocked = 12`, читать, не править). - **Арбитр гонок — тот же flock проекта**, что держит прогон: `backend/internal/store/store.go:55`=`EXCLUSIVE flock on`. Брать неблокирующе. ⚠ Это не перестраховка: движок пере-читает сид ПОСРЕДИ прогона, запись в живой прогон въехала бы в снапшот недетерминированно. - **Ответ несёт версию документа** (закон, п.3). ⚠ **Чего в этом паке НЕ делать:** не проектировать «глубину до черновика». Правка через `mined_delta` доезжает до редакторской волны и НЕ формирует черновик (`backend/internal/pipeline/seeding.go:140`=`The DRAFT wave selects over a BASE-scoped bank`), и это сознательно: механика пост-ридингового цикла прямым словом владельца не проектируется до итогов полигона. Дефолт версии 1 — редакторская глубина, и ответ глагола говорит это ЯВНЫМ полем. ### 4.3 Путь к файлу ключей аргументом — ДЕЛАЙ РОВНО ТАК Сегодня на SaaS движок не получает ключей: замысел «читает из `.env` рядом с `book.yaml`» записан комментарием платформы, но файл туда никто не кладёт. Дев-путь при этом наследует окружение платформы, прод-путь — нет, поэтому стенд зелёный, а прод голодает, и упасть не мог ни один тест. - Флаг **только у `translate`**. Читающим $0-глаголам ключи запрещены — они не должны их требовать вовсе: `backend/cmd/tmctl/dotenv.go:53-55`=`projections that must not demand keys at all` (D20.4). - **Деплой-файл грузится ПЕРВЫМ** в цепочке без перекрытия: явное бьёт конвенцию, а стендовые книги флага не передают и ничего не замечают. Текущая цепь — `backend/cmd/tmctl/main.go:184-185`=`loadDotEnv(filepath.Join(filepath.Dir(inv.cfgPath), ".env")`. - Названный и нечитаемый файл — громкий отказ конфигом при старте, не тихий пропуск. - ⚠ Ключи в проекте **общие на всех пользователей** (слово владельца 22.08); пер-книжные ключи возможны позже и ложатся в ту же форму. Ничего «на вырост» не строить. ### 4.4 Строгость загрузчика сида — ДЕЛАЙ РОВНО ТАК `backend/internal/membank/memseed.go:41`=`yaml.Unmarshal(raw, &sf)` — голый разбор без проверки имён ключей. Опечатка `gendr:` вместо `gender:` выбрасывается ДО валидации, и `seed-lint` её не видит: он проверяет значения, а не ключи. Прецедент строгости в проекте есть — `backend/internal/config/book.go:143`=`dec.KnownFields(true)`. Нужен и **тест на unknown-key**: сегодня в сид-линте нет ни одного такого кейса. ### 4.5 Путь артефакта и версия в `status --json` — РЕШИ САМ форму Платформа сегодня знает о твоей стороне ДВЕ вещи, и закрыть надо ОБЕ (строка 213): она сама вычисляет путь базы книги, дублируя движковую конвенцию, И знает суффикс твоего артефакта (`platform/internal/runner/artifacts.go:37`=`os.Open(db + ".bank.json")` — читать, не править, чужая зона). Отдай ей ПУТЬ БАНК-ЭКСПОРТА (и путь базы, если сочтёшь нужным) аддитивным полем — форму выбираешь ты. ⚠ Пере-именование банк-экспорта в фикс-имя — ЛОМАЮЩЕЕ, его окно — строка 161, сюда не тащить. Тем же касанием закрой асимметрию: банк-экспорт и манифест версию документа несут (`backend/internal/pipeline/bankexport.go:33`=`const bankExportVersion = "tm-bank-v1"`), а `status --json` — нет. ### 4.6 Дефолт корпуса в тест-хелперах — ДЕЛАЙ РОВНО ТАК (строка бэклога 218, добавлено 24.08) Три хелпера резолвят стендовый корпус от `$HOME/books`, которого на машине больше нет: `internal/miner/miner_parity_test.go:101-106`=`func standFile(parts ...string) string` · `internal/checks/labelharness_test.go:58-63`=`func standLabelsDir() string` · `internal/membank/labelharness_test.go:38`=`func memStandLabelsDir() string`. Правится ДЕФОЛТ, и ровно тем механизмом, который уже применён СОСЕДНЕЙ функцией в том же файле: `internal/miner/miner_parity_test.go:56-57`=`finds the repository root by MARKER` — корень ищется по маркеру вверх по дереву, а не считается точками и не берётся из `$HOME`. Env-переопределения (`TM_CHECKER_LABELS_DIR`, `TM_MINER_PARITY_{CONTRAST,RECORDS,SEED}`) уже существуют и остаются старшими — их не трогать. ⚠ **Проверено оркестратором 24.08 и это твоя опорная точка:** с переопределениями на новый путь корпус-батарея ЗЕЛЕНА, красен ровно `TestMinerFullBookParity` и ровно из-за отсутствующего `eval/exp16/data/jieba_dict_general_zh.txt` (строка 123, в git не попадает по построению). Значит критерий готовности пункта: `TM_MINER_PARITY=1 TM_CHECKER_LABELS=1 make battery-stand` БЕЗ единого `TM_*_DIR`-переопределения даёт ту же картину — всё зелено, кроме парити на jieba. Если краснеет что-то ещё — дефолт выведен неверно. ## 4.7 Деньги: платных вызовов в паке НОЛЬ — ДЕЛАЙ РОВНО ТАК Санкции на платные вызовы у пака нет и не запрашивается. Глагол банка $0 по построению. `--keys-file` доказывается юнит-тестами и пробой с ФЕЙКОВЫМИ ключами (проверяется, что путь резолвится, файл читается, порядок цепочки соблюдён), а не живым `translate`. **Рантайм-вердикт по платному пути остаётся PLAUSIBLE — это ожидаемый исход, а не дефект отчёта**; так и напиши. Упёрся в место, где без денег не доказать, — пинг с названной суммой, не трата. ## 5. Мандат самопроверки ИСПОЛНЕНИЕМ Не «перечитал» — **исполнил**. Минимум: - **Батарея целиком, ИЗ КАТАЛОГА `backend/`** после каждой содержательной правки: `go build ./...`, `go vet ./...`, `go test ./... -count=1`. ⚠ `go.mod` в корне репозитория НЕТ — модули раздельные, из корня батарея не соберётся. Платформенную батарею не гоняешь вовсе: платформу ты не правишь. Красный тест не «подгоняется»: править или удалять тест, голден или гейт ради зелени — НЕДОПУСТИМО; несогласие с тестом — пинг оркестратору, а не правка. - **Собственные адверсариальные посадки ВНЕ твоего списка.** Посади мутации в новый код и убедись, что батарея краснеет: дефолт срабатывает на НЕпустом ключе · частичная запись при отказе одной строки · повторный вызов пишет файл заново · глагол берёт лок блокирующе · читающий глагол требует ключи. ⚠ **Посадка обязана называть ПАКЕТ, в котором ищется пин** — не тот, где лежит правка: на этом уже горели, и ложное «мутация выжила» опаснее пропущенной, потому что выглядит как результат. - **Живая проба глагола на реальной книге стенда** (`<репозиторий>/books/gu-zhenren/*` — ⚠ каталог книг ПЕРЕЕХАЛ 24.08 внутрь репозитория и версионируется СВОИМ git; прежний адрес `~/books` мёртв, D39.157 п.2). Работать в КОПИИ вне рабочего дерева — транзиентные прогоны в чужом каталоге запрещены; правки в `books/` не делать вовсе, это не твоя зона. Проба $0: глагол денег не тратит. - **Субагенты РАЗРЕШЕНЫ явно** — на ревью своего кода, на поиск дефектов вне твоей карты, на опровержение твоих же выводов. Дефолт-запрет харнесса иначе тихо победит. - **Артефакт-находки обязателен:** `backend/docs/SEAM_PACK_FINDINGS.md` — что посадил, что краснело, что выжило. В репозитории, не вовне: доказательная база пака едет на приёмку вместе с деревом. ### 5.1 Обязательное для кодового пака - **Последний абзац отчёта — это план или обещание? Значит сделай его СЕЙЧАС**, а не оставляй следующей сессии. - **Дифф `^func Test` — исполнением, а не по памяти:** покажи командой, какие тесты добавлены и какие тронуты. - **Интервальная самоверификация субагентом** против ЯВНЫХ критериев — пак с новым глаголом заведомо длинный, и к его середине собственный фрейминг перестаёт быть виден изнутри. ## 6. Оси ревью (три, по характеру работы) 1. **Швовая** — у шва инвентарь ВСЕХ каналов другой стороны делается чтением ЧУЖОГО кода, не по памяти: что платформа реально зовёт и что реально читает. 2. **Общность** — заработает ли на паре, которой в репо нет, без правки Go. 3. **Деньги** — глагол обязан быть $0. Докажи это ледджером, а не рассуждением. Ты вправе ЗАМЕНИТЬ любую из осей своей, если аргументируешь, чем твоя лучше ловит дефекты этой работы. ## 7. Записка-план До кода — короткая записка `backend/docs/SEAM_PACK_PLAN.md` (в репозитории, не вовне): что делаешь, в каком порядке, какие развилки видишь. Комплектность против §4 сверяется МЕХАНИЧЕСКИ (таблицей или грепом), а не глазами. ## 8. Заявление = команда Каждое число и каждая категорика в отчёте — **с командой, которой получены, прямо рядом**. Не по памяти о том, как ты это выяснял. Приёмка пере-ранит выборочно; расхождение отчёта с пере-раном дороже, чем честное «не проверял». ## 9. Эхо-протокол старта ДО работы — не больше десяти строк: скоуп как ты его понял · инварианты, которые не тронешь · что делать НЕ будешь. Расхождение с этим промтом видно сразу и стоит дёшево. ## 10. Что НЕ удалось Обязательная секция отчёта. «Не проверено» ≠ «работает». Вердикт о рантайме без живого прогона — максимум PLAUSIBLE, и так и пиши. ## 11. Канал вопросов Конфликт промта с кодом или доками — **пинг через владельца, не интерпретация**. Ты вправе сказать «этого делать не надо» с аргументом: пак, который принёс развилку вместо тихой девиации, — хороший пак.