From dba8a0bb76857ac1a1359000ab1548d9c7283244 Mon Sep 17 00:00:00 2001 From: heaven Date: Sat, 5 Sep 2026 21:30:06 +0300 Subject: [PATCH] Add the chapter-structure pack: formats and an honest provenance vocabulary, with the cut gated on the stand book reproducing byte for byte --- docs/CHAPTER_STRUCTURE_SESSION_PROMPT.md | 374 +++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 docs/CHAPTER_STRUCTURE_SESSION_PROMPT.md diff --git a/docs/CHAPTER_STRUCTURE_SESSION_PROMPT.md b/docs/CHAPTER_STRUCTURE_SESSION_PROMPT.md new file mode 100644 index 00000000..825f5067 --- /dev/null +++ b/docs/CHAPTER_STRUCTURE_SESSION_PROMPT.md @@ -0,0 +1,374 @@ +# ПАК «ФОРМАТЫ И ЧЕСТНЫЙ СЛОВАРЬ» — движковая сессия + +> **ВЫДАН 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 — это `