19 KiB
Промт: движок, пак «писатель книги» — переведённая книга становится ФАЙЛОМ
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. Карта чтения — ПЯТЬ позиций, дальше только по их ссылкам
backend/internal/pipeline/export.go— прежде всего блок CAVEATS надExport: там названы три состояния, которые твой файл обязан не соврать (дрейф конфига, ghost-строки, pending-юниты).backend/cmd/tmctl/render.go:562-630— существующий рендер и то, чем--plaintextНЕ годится в качестве книги (аудит-баннеры, флаги в скобках).backend/internal/chunk/ingest.go— как книга РАЗБИРАЕТСЯ из epub: писатель обязан быть обратим к этому чтению, иначе круг «загрузил → получил» не замыкается.docs/architecture/12-go-style-notes.md§0 — норматив общности: пары языков это ДАННЫЕ.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:137 → ingest/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/ — зона
оркестратора»).