# ПАК «ФОРМАТЫ И ЧЕСТНЫЙ СЛОВАРЬ» — движковая сессия > **ВЫДАН 05.09 сессии `textmachine-main-fe`.** Три круга совместного ревью (оркестратор + Fable 5); > состав пака и словарь — итог восьми кругов сходимости, тела разборов в `D39.202`/`D39.207`. **Зона записи:** `backend/`. **Коммитит оркестратор** (`textmachine-main-27`), ты готовишь дерево и передаёшь. **Отчёт:** секция «Бэкенд» в `docs/PROGRESS.md`. **Денег:** $0, ни одного платного вызова. --- ## 0. КАРТА ЧТЕНИЯ — ПЯТЬ ПОЗИЦИЙ, ОНА ЗАКОН Дальше — только по её ссылкам. Голову журнала решений целиком НЕ читать (канон это запрещает). 1. **`backend/internal/chunk/ingest.go`** целиком — предмет пака живёт здесь. 2. **`docs/research/27-chapter-detection.md`** — через его ревью-шапку. Дизайн НЕ ратифицирован; это материал, а не заказ. 3. **`## D39.205`** (пропущенный кусок — критическая поломка) и в `## D39.202` — **только п.3-бис** (гейт умирает вытеснением) **и п.9** (чего не сторожит ничто). Тело ноты целиком не нужно. ⚠ **ПОДНОМЕР своего тела не имеет** — `D2.3` живёт пунктом внутри `^## D2`. 4. Строки бэклога **297** (словарь провенанса) и **299** (имя, не разрешающееся ни во что) в `docs/PROGRESS.md`. 5. **`docs/architecture/12-go-style-notes.md`** — норматив стиля, только больные места. **Код читаешь по мере правки. Несущие файлы, которые ты ТОЧНО будешь править:** `chunk/ingest.go` · `pipeline/manifest.go` (ключ, тег, `title_raw`) · `chunk/chunker.go` (`stripHeading`, `matchHeaderLine`) · `lang/langpack.go` (⚠ `parseHeading` СЕГОДНЯ ТРЕБУЕТ `marker`, `:677-679` — после «только `template`» он упадёт, это заказанная правка) · `lang/embedded.go`. --- ## 1. ЧТО УЖЕ ПОСТРОЕНО — НЕ ЗАКАЗЫВАТЬ ЗАНОВО Самый дорогой раздел: половина ошибок прошлых паков — постройка второго носителя. - **Провенанс разреза уже есть** — поле `Document.Structure`, и оно **спроектировано верно**: отвечает на «КАКОЙ ПУТЬ провёл границы» (`ingest.go:94-95`). ⛔ Неверна его первая строка (`:70`), рекламирующая «how much the chapter cut can be trusted», — и именно её списала платформа. **Поле не переделывать; чинить словарь и формулировку.** - ⛔ **Валидатор V2-7 «формат провёл ВСЕ границы» (`ingest.go:170-176`) — ЗАМЕЩАЕТСЯ конкуренцией (§2.4), а не сохраняется рядом с ней.** После конкуренции его предикат (`len(parts)>1 && len(chapters)==len(parts)`, `:177`) становится мёртвым кодом. Не пытайся сохранить обе логики — это прямо противоречащие друг другу правила. - **Доминирующий юнит с полом ≥2** для CJK уже есть (`detectChapterUnit`, `ingest.go:249-263`) — это мини-индуктор, а не список паттернов. **Пол ≥2 — единственный валидатор, живущий в паке 1.** - **Читательские слова цели** уже разведены и грузятся СВОИМ загрузчиком по ЯЗЫКУ, вне пар-пака (`lang.LoadReaderWords`, `reader.go:87`) — **прецедент, по образцу которого делается структура источника.** - **Скрипт языка** уже выводится (`lang.LangScripts`, `script.go:67`), CJK-дефолт живёт данными. --- ## 2. ЧТО ЗАКАЗАНО ### 2.1 Шаг 0 — ПРИБОР ДО ПРАВОК. Поведение не меняется, $0 Без него пак не имеет права ложиться: под ним подписанный банк владельца (окна в ординалах глав) и боевая книга. 1. ⛔ **Пин — ПОЛНЫЙ МАНИФЕСТ до правок**, а не хеш: id глав + номера (лучше весь JSON). Из хеша не построить карту сдвигов §2.5, а она заказана. Хеш — производное, снимай и его. ⛔ **НО: под `books/` не запускай НИЧЕГО.** `tmctl manifest` пишет сайдкар рядом с проектной базой книги (`manifestPath()` = `ProjectDB + ".manifest.json"`, `pipeline/manifest.go:189`), то есть ПИШЕТ в каталог книги — а запись там запрещена (§7). **Форма: копируешь исходник и `book.yaml` в собственный каталог сессии** (не `/tmp` — см. §4, не `books/`) **и все замеры и пины делаешь на копии.** ⛔ **В копии обязательно перенаправь `project_db` и `source_file`.** `project_db` — ЯВНОЕ поле yaml (`config/book.go:86`), и «рядом с yaml» подставляется, только если оно ПУСТО (`:199-201`); у стендовых книг там абсолютные пути. Скопируешь как есть — сайдкар уедет обратно под `books/` (запись, которая запрещена) или по мёртвому пути. ⚠ **А `langpack_root` / `langpack_extend` НЕ трогай** — они только читаются, и подмена дала бы другой `Pack.Version()`, то есть другой `cutTag`: пин снялся бы с другого разреза, и сравнивать было бы нечего. 2. **Синтетические фикстуры по стандартам:** EPUB 3 с `nav`, EPUB 2 с NCX. Сегодня тестовый билдер пишет только `container→OPF→spine→xhtml` и **nav/NCX не пишет вовсе** (`chunktest/epub.go:42-82`). 3. **Оракул — КОД, не замер:** EPUB с `nav` разворачивается в txt, путь разреза обязан восстановить nav-границы. Ground truth **без ручной разметки и без единого вызова модели**. ⚠ Сам ЗАМЕР — пак 2. 4. **Замеры ДО правок, каждый числом в отчёт:** есть ли `\f` в стенд-книгах · есть ли главы-огрызки от print-TOC · сбрасывается ли нумерация по томам. **Файлы стенда — поимённо** (см. §7 о доступе): 蛊真人 (GB18030 txt, ~2283 «第N节») · EPUB владельца Кристофф/en · EPUB владельца isekai/ja. ⚠ Платная книга платформы `bk_SS5VES2JELESJSTR` живёт в workdir платформы, **гейту §3 не видна и в него не входит**; её заметки теряются по слову оркестратора. ### 2.2 Данные структуры — ПО ИСТОЧНИКУ, вне пар-пака Ревью-вопрос проекта: «заработает ли пара, которой в репо НЕТ, без правки Go?» - `marker`/`units`/выбор читателя номеров/грамматика шаблона → **файл структуры ИСТОЧНИКА**, свой загрузчик по `Book.SourceLang`, **по образцу `reader.txt`**. - ⛔ **В пар-пак класть НЕЛЬЗЯ:** книга без каталога пары идёт с nil-паком (`runner.go:397-407`). - ⛔ **ОДИН ИСТОЧНИК НА ФАКТ.** Ингест И `stripHeading` чанкера читают **тот же самый файл**. Сегодня чанкер берёт `HeadingRule` пары; после разведения `heading.txt` оставляет ТОЛЬКО `template`. Оставить marker в обоих местах = вернуть дрейф ingest↔chunker, который pair-14 закрыл («ONE source, no ingest↔chunker byte-drift», `ingest.go:181-186`). - **Лестница при отсутствии данных:** язык → скрипт → embedded-дефолт ПО СКРИПТУ → честное `none`. ⛔ **Дефолт НЕЛЬЗЯ класть под `internal/lang/data/`**, хотя прецедент `cjk-section.txt` туда и манит: `EmbeddedVersion()` хеширует **весь каталог** (`embedded.go:44-56`, `ReadDir("data")`; «a byte edit of ANY embedded file is a loud --resnapshot»). Положишь туда — нарушишь «не в снапшот» МЕХАНИЗМОМ, тест §4 п.3 покраснеет, и самый соблазнительный «фикс» будет хаком (руками исключить файл из обхода). Клади в ОТДЕЛЬНЫЙ embed-каталог со своей версией (`internal/lang/structure/` или как назовёшь). - **Объём читателей номеров в паке 1 — РОВНО СУЩЕСТВУЮЩИЙ** (арабские + CJK). Схема данных пишется под общий случай, но **римские/словесные читатели НЕ строятся** — их заказывает пак 2, если замер покажет нужду. ⛔ **ВЕРСИОНИРОВАНИЕ — ТОЧНО, иначе воспроизводится F1 MAJOR (D39.122).** Данные структуры решают ГРАНИЦЫ ⇒ обязаны войти в **`cutInputs`** (`manifest.go:240-255`) ⇒ двигают **`cutTag`** ⇒ двигают **unit-id**. `manifestKey` фолдит компоненты ПОРОЗНЬ (`manifest.go:336-354`), и одного его НЕДОСТАТОЧНО: правка данных перережет книгу, а id юнитов переживут — ровно тот дефект, ради которого тег и заведён. **В снапшот волн — НЕ класть.** Тест `TestEveryCutInputMovesTheTag` (`cuttag_test.go:26`) получает новый компонент. **ФОРМА компонента — не на твоё усмотрение, она уже задана прецедентом:** `Structure: <версия парсера> + sha256(байты структурных данных, РЕАЛЬНО загруженных для этой книги — `/structure.txt` либо дефолт по скрипту)`. **Никогда `omitempty`** — дефолт есть всегда. Считается ОДИНАКОВО на пишущем и читающем пути (`runner.go:384-386`: «loaded on BOTH the write path and the read-only path so status/export/report reproduce the identical snapshot»). Это ровно форма `Langpack: r.packVersion()`. ⛔ **ЧТО НЕ ПЕРЕЕЗЖАЕТ, и это тихая ловушка:** **числительные** (`Zero`/`Digit`/`Unit`/`Magnitude`) **остаются в `data/cjk-section.txt`**. Их читает не только ингест — на них стоят ДЕНЕЖНЫЕ $0-гейты величины чисел (`checks/cheapgates.go:601, 636, 649, 666` — `lang.DefaultCJKSection()`), а это ВЕРДИКТНЫЕ данные, и им место в снапшоте. Уносишь числительные — **молча меняются вердикты гейтов**. Переезжают ТОЛЬКО `marker` / `units` / грамматика шаблона. ### 2.3 EPUB по стандарту - Читать **`nav` (EPUB 3) и NCX (EPUB 2)**; spine — порядок ЧТЕНИЯ, а не оглавление. - Учитывать `linear` и `properties` — сегодня `opfPackage` их не разбирает (`ingest.go:500-509`). **Нормализация nav — правилами, а не на усмотрение** (research/27 — материал, не заказ): цели сортируются **по позиции в spine** · цели вне spine или неразрешимые — игнор со счётом · вложенные `ol` **сплющиваются** (каждая цель — граница; иерархия томов — §5) · дубли целей схлопываются · приоритет **nav → NCX → spine** · nav с нулём разрешимых целей = отсутствует. ⛔ **ТЕКСТ КНИГИ НЕ ТЕРЯЕТСЯ НИКОГДА** (`D39.205`: пропущенный кусок — критическая поломка): - **служебные документы — НЕ текст книги и исключаются со счётом**, и список ТОЧНЫЙ, потому что наивная форма не сработает: исключаются (1) документ с `properties="nav"`; (2) документы, чья роль — `toc` или `cover` — объявлена **в landmarks (EPUB 3) ЛИБО в OPF `` через `` (EPUB 2)**. ⛔ Обе формы обязательны: landmarks — конструкция EPUB 3, **у EPUB 2 nav-документа нет вовсе**, и сессия, проверившая только landmarks, приклеит `toc.xhtml` к главе 1 ровно на том «массовом EPUB 2», которым это правило себя и мотивирует. ⚠ `properties="cover-image"` стоит на ИЗОБРАЖЕНИИ, а не на документе, и среди документов spine не исключит ничего; landmarks — это `