textmachine/docs/BACKEND_SEAM_PACK_SESSION_PROMPT.md

18 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/ — чужие зоны, правки в них запрещены. Ты не коммитишь — дерево готовишь и передаёшь на лендинг оркестратору (канон git-норм — CLAUDE.md). git add -A, git add ., git commit -a запрещены в любом случае. В дереве живёт незакоммиченная работа параллельных сессий — не трогай её и не «прибирай». ⚠ Пинги и итоги — в docs/PROGRESS.md, секция «Бэкенд» (у зоны бэкенда своего журнала нет).

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 — предметные хвосты твоего пака.
  4. docs/experiments/00-provider-quirks.md — обязательное пре-чтение перед любым касанием вызовов провайдеров (тебя касается через --keys-file).
  5. docs/architecture/12-go-style-notes.md — норматив общности: Go-логика НЕ ветвится по паре и книге. Ревью-вопрос по умолчанию: «заработает ли на паре, которой в репо ещё НЕТ, без правки Go?»

Код первичен. Все file:line ниже — отправные точки, а не истина: открывай и проверяй сам. Всю документацию этого проекта писали нейросети, она ошибается, и уже ошибалась в этих самых местах.

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.
  • Конфликт с подписанной строкой сида — отказ С ИМЕНЕМ, молча принять нельзя: это заминирует следующий прогон.
  • Проекция ДО мутации. Мутирующий глагол сначала говорит, что произойдёт и почём.
  • Занятый лок — отказ РОВНО классом 12, не 10. Полоса различает их не косметически: 10 значит «деплой сломан, чинит человек, книги ждут», 12 значит «ничего не произошло, подожди и повтори» (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). Отдай ей путь — аддитивным полем, форму выбираешь ты. Тем же касанием закрой асимметрию: банк-экспорт и манифест версию документа несут (backend/internal/pipeline/bankexport.go:33=const bankExportVersion = "tm-bank-v1"), а status --json — нет.

5. Мандат самопроверки ИСПОЛНЕНИЕМ

Не «перечитал» — исполнил. Минимум:

  • Батарея целиком после каждой содержательной правки: go build ./..., go vet ./..., go test ./... -count=1. Красный тест не «подгоняется»: править или удалять тест, голден или гейт ради зелени — НЕДОПУСТИМО; несогласие с тестом — пинг оркестратору, а не правка.
  • Собственные адверсариальные посадки ВНЕ твоего списка. Посади мутации в новый код и убедись, что батарея краснеет: дефолт срабатывает на НЕпустом ключе · частичная запись при отказе одной строки · повторный вызов пишет файл заново · глагол берёт лок блокирующе · читающий глагол требует ключи. ⚠ Посадка обязана называть ПАКЕТ, в котором ищется пин — не тот, где лежит правка: на этом уже горели, и ложное «мутация выжила» опаснее пропущенной, потому что выглядит как результат.
  • Живая проба глагола на реальной книге стенда (~/books/gu-zhenren/*, работать в КОПИИ вне рабочего дерева — транзиентные прогоны в чужом каталоге запрещены). Проба $0: глагол денег не тратит.
  • Субагенты РАЗРЕШЕНЫ явно — на ревью своего кода, на поиск дефектов вне твоей карты, на опровержение твоих же выводов. Дефолт-запрет харнесса иначе тихо победит.
  • Артефакт-находки обязателен: ~/tm-handoff/backend-seam-pack/findings.md — что посадил, что краснело, что выжило. Абсолютный путь, не скрэтчпад.

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

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

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

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

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

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

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

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

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

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

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

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