textmachine/docs/BACKEND_SEAM_PACK_SESSION_PROMPT.md

215 lines
23 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)
> Выдан оркестратором №19, 23.08.2026. Активный промт зоны бэкенда — один. Параллельно законен
> только `platform/docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md` (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`, секции «Бэкенд», висят два пинга твоей зоне. **Ни один из них в этот пак не входит:**
пинг про потерю инъекции банка на эскалации в 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 Деньги: платных вызовов в паке НОЛЬ — ДЕЛАЙ РОВНО ТАК
Санкции на платные вызовы у пака нет и не запрашивается. Глагол банка $0 по построению. `--keys-file`
доказывается юнит-тестами и пробой с ФЕЙКОВЫМИ ключами (проверяется, что путь резолвится, файл читается,
порядок цепочки соблюдён), а не живым `translate`. **Рантайм-вердикт по платному пути остаётся PLAUSIBLE —
это ожидаемый исход, а не дефект отчёта**; так и напиши. Упёрся в место, где без денег не доказать, —
пинг с названной суммой, не трата.
## 5. Мандат самопроверки ИСПОЛНЕНИЕМ
Не «перечитал» — **исполнил**. Минимум:
- **Батарея целиком, ИЗ КАТАЛОГА `backend/`** после каждой содержательной правки: `go build ./...`,
`go vet ./...`, `go test ./... -count=1`. ⚠ `go.mod` в корне репозитория НЕТ — модули раздельные, из корня
батарея не соберётся. Платформенную батарею не гоняешь вовсе: платформу ты не правишь. Красный тест не «подгоняется»: править или удалять тест, голден или гейт
ради зелени — НЕДОПУСТИМО; несогласие с тестом — пинг оркестратору, а не правка.
- **Собственные адверсариальные посадки ВНЕ твоего списка.** Посади мутации в новый код и убедись,
что батарея краснеет: дефолт срабатывает на НЕпустом ключе · частичная запись при отказе одной
строки · повторный вызов пишет файл заново · глагол берёт лок блокирующе · читающий глагол требует
ключи. ⚠ **Посадка обязана называть ПАКЕТ, в котором ищется пин** — не тот, где лежит правка:
на этом уже горели, и ложное «мутация выжила» опаснее пропущенной, потому что выглядит как результат.
- **Живая проба глагола на реальной книге стенда** (`~/books/gu-zhenren/*`, работать в КОПИИ вне
рабочего дерева — транзиентные прогоны в чужом каталоге запрещены). Проба $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. Канал вопросов
Конфликт промта с кодом или доками — **пинг через владельца, не интерпретация**. Ты вправе сказать
«этого делать не надо» с аргументом: пак, который принёс развилку вместо тихой девиации, — хороший пак.