textmachine/docs/BACKEND_BOOKWRITER_PACK_SESSION_PROMPT.md

19 KiB
Raw Blame History

Промт: движок, пак «писатель книги» — переведённая книга становится ФАЙЛОМ

0. Какая проблема и что решит твой результат

Проект переводит книги и не умеет отдать книгу. Ни одной стороной. Движок отдаёт только поток: renderExport (backend/cmd/tmctl/render.go:565) печатает стабильный JSON tm-export-v1 либо --plaintext с аудит-баннерами === CHAPTER N CHUNK M ===. Греп epub|fb2|docx по backend/** минус тесты даёт 43 попадания, из них 29 в internal/chunk/ ingest.go. ⚠ И писатель EPUB в дереве УЖЕ ЕСТЬbackend/internal/chunk/chunktest/epub.go:37-112 (BuildEPUB/BuildEPUBAt): настоящий OCF-контейнер, mimetype первым и zip.Store, container.xml, content.opf с manifest+spine, XHTML. Он ТЕСТОВЫЙ, но обратим ингесту по построению и запинен (ingest_test.go:537 TestEPUBFixtureMimetypeIsOCFConformant). Его собственная шапка предупреждает: дублирование zip/OPF-строителя развело бы две копии. Начинать с него, а не с нуля. Платформа обещанные каноном ручки выдачи не монтирует, и её export_formats захардкожен пустым.

Что решит твой результат: переведённую книгу можно будет открыть в читалке. Сегодня цель №1 канона («издательское художественное качество») не проверяется ничем на выходе: результат некуда открыть. Носитель — строка 236 единого бэклога, вес «блокер-очереди», пункт 1 очереди №20.

Чего твой результат НЕ решает: дверь платформы (createExport/getExport) — отдельная работа ПОСЛЕ живого прогона, потому что до него неизвестно, что реально кладётся в файл.

1. Зона записи и git

Пишешь только в backend/. Не коммитишь: дерево готовишь и передаёшь оркестратору. Канон git — в CLAUDE.md, здесь не дублируется. Чужие незакоммиченные файлы не трогать.

2. Карта чтения — ПЯТЬ позиций, дальше только по их ссылкам

  1. backend/internal/pipeline/export.goпрежде всего блок CAVEATS над Export: там названы три состояния, которые твой файл обязан не соврать (дрейф конфига, ghost-строки, pending-юниты).
  2. backend/cmd/tmctl/render.go:562-630 — существующий рендер и то, чем --plaintext НЕ годится в качестве книги (аудит-баннеры, флаги в скобках).
  3. backend/internal/chunk/ingest.go — как книга РАЗБИРАЕТСЯ из epub: писатель обязан быть обратим к этому чтению, иначе круг «загрузил → получил» не замыкается.
  4. docs/architecture/12-go-style-notes.md §0 — норматив общности: пары языков это ДАННЫЕ.
  5. docs/PROGRESS.md строка 201 (синтетический заголовок едет внутри текста юнита) — твоя развилка, см. §3.3.

3. Работа

3.1 Что построить (делай РОВНО так)

Движковый писатель книги из BookExport, вызываемый из tmctl. Минимум два формата: EPUB (книга для читалки) и чистый текст — без аудит-баннеров, без флагов в скобках, без служебных маркеров. Существующий --plaintext не трогать и не переименовывать. ⚠ Обоснование именно такое, а не обратное: eval/README.md:80 говорит «--plaintext для глаз, дефолтный JSON для скриптов» — то есть машинная поверхность это JSON, и у него ДВА потребителя: полигон и платформа (platform/internal/runner/ engine.go:137ingest/export.go DecodeExport; STACK_DECISIONS.md:376 называет его «единственным каналом, несущим ТЕКСТ пары»). Менять форму tm-export-v1 — значит менять платформу из чужой зоны.

Файл обязан быть ДЕТЕРМИНИРОВАН: два прогона на одних данных дают побайтово одинаковый файл. EPUB — это zip, и в нём по умолчанию лежат時metки и порядок записей; если они текут, детерминизм проекта (цель №6) кончается на последнем шаге. Докажи прогоном, а не рассуждением.

3.2 Три состояния, о которых файл не имеет права соврать (делай РОВНО так)

Export их уже вычисляет — не изобретай заново, но и не игнорируй:

  • PendingUnits > 0 — книга переведена НЕ ЦЕЛИКОМ. Файл, молча притворяющийся полным, — худший исход этого пака.
  • GhostRows > 0 — есть строки вне текущего манифеста.
  • ConfigDrift == true — текущий конфиг рендерит не тот снапшот, что несут строки.

Как именно это показать читателю — решаешь сам и аргументируешь (отказ писать · писать с явной пометкой · отдельный флаг команды). Что НЕ обсуждается: молчаливого полного файла на неполной книге быть не может.

3.3 Синтетический заголовок — НЕ твоя развилка (делай РОВНО так)

Заголовок «Глава N» сегодня клеится ВНУТРЬ текста юнита — export.go:266, и ещё в ДВУХ точках waverun.go:647 и waverun.go:721 (третья — путь draft-only отгрузки). Инвариант, который они держат и который назван в самом коде: tmctl export и tmctl translate отгружают побайтово одинаковый текст.

Трогать ApplyHeading и любую из трёх точек ЗАПРЕЩЕНО. Дизайн глав НЕ ратифицирован (D39.122 п.2д: heading манифеста — ВРЕМЕННЫЙ рендер движка), а форма для него уже назначена: строка бэклога 160 «Структура глав, Этап 0», гейт которой пал словом владельца 15.08 (D39.136 п.3) — она едет ОТДЕЛЬНЫМ паком с мандатом максимального качества. Строка 201 — её родня и стоит «когда-нибудь (с 160)». Этот пак её не решает и не предвосхищает.

Что делать вместо: заголовок для оглавления брать из УЖЕ персистированного манифеста — ManifestChapter.Heading (backend/internal/pipeline/manifest.go:108), тот же детерминированный литерал. Форма tm-export-v1 не меняется, разбирать ничего не надо, оглавление есть. Если текст юнита при этом начинается тем же заголовком — срезать ИЗВЕСТНЫЕ байты префикса (ApplyHeading — чистая склейка heading + "\n\n" + text, chunker.go:186-192), а не угадывать эвристикой. ⚠ Если найдёшь причину, по которой так нельзя, — пинг, а не своё решение: вопрос гейчен владельцем.

3.3-бис Метаданных книги в BookExport НЕТ — учти до кода

Структура несёт Version, BookID, TotalUnits, PendingUnits, GhostRows, ConfigDrift, CurrentSnapshot, Chunks — и ни заголовка, ни языка, ни автора. EPUB требует dc:title, dc:language, dc:identifier. Первые два достижимы из config.Book (book.go:25-27: Title, SourceLang, TargetLang); автора в проекте нет вовсе — источника для dc:creator не существует, и выдумывать его не надо. dc:language берётся из TargetLang, а не литералом: пары — данные. Решаешь сам: расширять ли BookExport (⚠ добавление поля безопасно — платформа декодирует подмножеством и неизвестные поля игнорирует; ПЕРЕНОС существующего поля — нет) или брать метаданные мимо него.

3.4 Куда пишется файл — размечено, не додумывай

tmctl сегодня не пишет НИ ОДНОГО файла: греп os.Create|os.WriteFile по cmd/tmctl/*.go минус тесты пуст, всё идёт в stdout. Значит выходная поверхность — новая. Реши и аргументируй: флаг пути или stdout · поведение при существующем файле (перезапись или отказ) · один файл на книгу или на главу. Молча выбранная форма здесь дороже неверной: её потом читает платформенная дверь выдачи.

3.5 Границы

  • Платформу не трогать вообще. Её дверь — следующая работа, не твоя.
  • Формат tm-export-v1 не менять без явного довода: его потребляет полигон.
  • Пары языков — данные. Ревью-вопрос: «заработает ли пара, которой в репо НЕТ, без правки Go?» Если твой писатель знает слово «Глава» или порядок имени автора — это утечка.
  • Внешние зависимости: EPUB это zip + XHTML, и стандартная библиотека это умеет. Новая зависимость ради формата — решение с доводом и строкой в STACK_DECISIONS, а не побочный эффект.

4. Самопроверка ИСПОЛНЕНИЕМ (без неё работа не принята)

  • ДАННЫЕ: coldrun-a НЕ переведена — не строй на ней доказательство. Проверено: у неё только волна draft (20 строк), финальной стадии нет ни у одного юнита, экспорт даёт 14/14 pending и CONFIG-DRIFT. Вдобавок её конфиги несут абсолютные пути /home/ubuntu/… от прежней машины и ретайрнутые ключи, а схема БД v14 против головы v16 — tmctl export там падает четырьмя разными отказами подряд. Годные книги с волной edit: books/gu-zhenren/minirun (draft 20 / edit 14, схема v10) и books/gu-zhenren/acceptance (57/57, v7) — обе требуют миграции до v16. books/ — отдельный git-репозиторий со своим origin: работай на КОПИИ в песочнице, оригиналы не мигрируй и не правь.
  • Файл открывается читалкой — если есть чем. Проверено: на этой машине нет ни calibre, ни epubcheck, ни java, ни python-библиотек epub. Значит ось «издательский результат» механического верификатора пока не имеет: либо приноси свой (валидатор в песочнице), либо честно пиши в obstacle, что проверено структурно, а не читалкой. Не выдавай второе за первое.
  • Круг замыкается: файл, который ты написал, скорми обратно internal/chunk/ingest.go и покажи, что он разбирается. Это единственная механическая проверка, что писатель обратим чтению.
  • Детерминизм: два прогона → cmp побайтно. Покажи команду и результат.
  • Три состояния §3.2 — по посадке на каждое: неполная книга, ghost-строка, дрейф конфига. Каждая обязана отработать так, как ты решил, и это видно в файле или в отказе.
  • Править или удалять тест ради зелени НЕДОПУСТИМО (D39.121). Это не абстракция: любой вариант, трогающий заголовок, красит backend/internal/chunk/chunker_heading_test.go:121, и самый дешёвый способ вернуть зелень — правка ассерта. Несогласие с тестом — пинг, а не правка.
  • Дифф ^func Test — ИСПОЛНЕНИЕМ, не памятью: покажи командой, что ни один тест не исчез.
  • Интервальная самоверификация (D39.121): пак длинный, поэтому в СЕРЕДИНЕ работы — проход субагента по готовой части против явных критериев, а не только в конце.
  • make battery целиком, не go test: линтер идёт ДО тестов, и на этом уже попались двое. -race обязателен.
  • Разрешаю субагентов (иначе дефолт харнесса тихо запретит). Модель задавай ЯВНО и знай, сколько их работает. Трудный вопрос — старшей модели.
  • Адверсариальный проход по своей готовой работе перед сдачей. Направление: файл, молча притворяющийся полным; недетерминизм zip; заголовок, потерянный или задвоенный; кодировка и направление письма; книга из одного юнита и книга из нуля юнитов. Пока идёт проход — дерево не двигается: либо морозь, либо называй агенту коммит.

5. Оси ревью

Издательский результат (файл открывается и читается как книга) · честность выдачи (неполнота никогда не выглядит полнотой) · детерминизм (побайтовая воспроизводимость). Вправе заменить ось с аргументом.

6. Записка-план и комплектность

До кода — короткая записка-план в docs/PROGRESS.md, секция «Бэкенд»: что берёшь, что не берёшь, чем докажешь. Перед сдачей — механическая сверка состава против §3 таблицей: пункт → сделано/нет → чем доказано. Пропуск подписывается пропуском. Последний абзац отчёта оказался планом или обещанием? Сделай сейчас.

7. Канал вопросов и право отказаться

Конфликт промта с кодом или доками — пинг через владельца, не интерпретация. Есть право сказать «этого делать не надо» с аргументом.

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

Любое число и любая категорика — с командой, которой получены. Приёмка пере-ранит: клейм без пере-прогона записывается как «со слов сессии».

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

ДО первой правки — ≤10 строк: скоуп / инварианты / чего не делаешь. Отправь первым действием по адресу оркестратора из /tmp/textmachine-channel (механизм — CLAUDE.md; впиши туда СВОЙ блок первым делом). Канала нет ⇒ НЕ искать: вопрос секцией в отчёт, работа продолжается.

10. Obstacle reporting

Обязательная секция: что НЕ удалось и что осталось непроверенным. Пустой не бывает. Три исхода: «подтверждено» / «опровергнуто» / «не проверено» — третье не сваливать во второе.

11. Журнал

Итоги и вопросы — docs/PROGRESS.md, секция «Бэкенд» (это единственное исключение из «docs/ — зона оркестратора»).