textmachine/docs/BACKEND_SEAM_PACK_SESSION_PROMPT.md

23 KiB
Raw Blame History

Промт: БЭКЕНД-ПАК «ВХОДНАЯ ДВЕРЬ ШВА» (движковая половина 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 ниже — отправные точки, а не истина: открывай и проверяй сам. Всю документацию этого проекта писали нейросети, она ошибается, и уже ошибалась в этих самых местах.

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: глагол денег не тратит.
  • Субагенты РАЗРЕШЕНЫ явно — на ревью своего кода, на поиск дефектов вне твоей карты, на опровержение твоих же выводов. Дефолт-запрет харнесса иначе тихо победит.
  • Артефакт-находки обязателен: ~/tm-handoff/backend-seam-pack/findings.md — что посадил, что краснело, что выжило. Абсолютный путь, не скрэтчпад.

5.1 Обязательное для кодового пака

  • Последний абзац отчёта — это план или обещание? Значит сделай его СЕЙЧАС, а не оставляй следующей сессии.
  • Дифф ^func Test — исполнением, а не по памяти: покажи командой, какие тесты добавлены и какие тронуты.
  • Интервальная самоверификация субагентом против ЯВНЫХ критериев — пак с новым глаголом заведомо длинный, и к его середине собственный фрейминг перестаёт быть виден изнутри.

6. Оси ревью (три, по характеру работы)

  1. Швоваяу шва инвентарь ВСЕХ каналов другой стороны делается чтением ЧУЖОГО кода, не по памяти: что платформа реально зовёт и что реально читает.
  2. Общность — заработает ли на паре, которой в репо нет, без правки Go.
  3. Деньги — глагол обязан быть $0. Докажи это ледджером, а не рассуждением.

Ты вправе ЗАМЕНИТЬ любую из осей своей, если аргументируешь, чем твоя лучше ловит дефекты этой работы.

7. Записка-план

До кода — короткая записка ~/tm-handoff/backend-seam-pack/plan.md: что делаешь, в каком порядке, какие развилки видишь. Комплектность против §4 сверяется МЕХАНИЧЕСКИ (таблицей или грепом), а не глазами.

8. Заявление = команда

Каждое число и каждая категорика в отчёте — с командой, которой получены, прямо рядом. Не по памяти о том, как ты это выяснял. Приёмка пере-ранит выборочно; расхождение отчёта с пере-раном дороже, чем честное «не проверял».

9. Эхо-протокол старта

ДО работы — не больше десяти строк: скоуп как ты его понял · инварианты, которые не тронешь · что делать НЕ будешь. Расхождение с этим промтом видно сразу и стоит дёшево.

10. Что НЕ удалось

Обязательная секция отчёта. «Не проверено» ≠ «работает». Вердикт о рантайме без живого прогона — максимум PLAUSIBLE, и так и пиши.

11. Канал вопросов

Конфликт промта с кодом или доками — пинг через владельца, не интерпретация. Ты вправе сказать «этого делать не надо» с аргументом: пак, который принёс развилку вместо тихой девиации, — хороший пак.