# Малая пачка: ingest грязных епабов (14а) + комментарии промпт-файлов (14б)
**База:** записка оркестратора №8 от 30.07, санкции владельца 26.07 (бэклог `docs/PROGRESS.md`, строки 14а/14б).
**Зона:** `backend/`. **Деньги сессии: $0** — ни одного вызова провайдера, вся приёмка на локальном сырье.
**Статус:** построено и проверено исполнением. Сессия НЕ коммитит, лендинг — оркестратора.
> **Ревью-шапка (оркестратор №9, 30.07): ПРИНЯТО И ЗАЛЕНДЕНО, D39.54.** Приёмка исполнением на
> неподвижных копиях (старая = HEAD, новая = деливерабл): собственный дампер old-vs-new по трём епабам
> стенда — 205 документов, дифф глав и ruby ПУСТ (3.29 МБ на сторону); полный сьют `-race -count=1`
> 14/14 зелёный, gofmt/vet чисто; четыре спот-мутации вне порядка сессии (D — SHA по сырому, M8 —
> нейтрализация вырезания, M7 — raw-text везде, M10 — flushRuby в no-op) — все КРАСНЫЕ, восстановление
> зелёное. Финальная сверка сессии (§2.8, несбалансированный ruby — третий улов ревью) вошла в приёмку:
> копии сняты ПОСЛЕ фикса, мутационная таблица сессии 17/17. Посылки
> перемерены независимо: 126 itemref (не 958) · 8/8 книг стенда txt · ровно 2 сущности из 252 расходятся
> (кросс-чек через WHATWG-таблицу python и GOROOT-таблицу `xml.HTMLEntity`) · цитата `snapshot.go:138`
> дословна · grep `terminolog` по `snapshot.go` пуст. Диспозиции: CDATA (§7.4) — В СКОУПЕ (без неё
> переезд = регрессия на валидном XHTML); chunkerVersion НЕ поднят — ратифицировано D39.54 (экспозиция —
> пустое множество); mimetype — верность формату, не фикс поведения (§7.5 принят, в D-ноте так и
> записано). Фикс-лист (1): комментарий поля `SHA256` в `render.go:89` остался «hash of the raw file» —
> одна строка, на ближайшее касание. Кандидат §8 (`` без XML-объявления) — бэклог 34а.
> **Две поправки к посылкам записки — читать до §1.** Обе найдены замером, обе меняют формулировки
> приёмки, ни одна не меняет решение строить.
>
> 1. **«Empire of the Dawn, 958 документов spine» — неверно.** В книге **126** `` (288 записей
> zip, 283 `- ` манифеста, из них 154 `.jpg`). Число 958 не соответствует ничему в файле. Сравнение
> проведено на **всех трёх** епабах стенда — 205 документов суммарно.
> 2. **«Ingest выполняется при создании книги — существующие БД и снапшоты не трогаются» — обоснование
> неверно, вывод верен.** `TranslateBook` вызывает `chunk.IngestEncoded` и `SplitChunks` на **КАЖДОМ
> прогоне** (`bookrun.go:112,140`; то же в `status.go:190,194`) — источник переизвлекается и
> перерезается каждый раз. Существующие БД защищены другим фактом: **все книги стенда — txt/gb18030**
> (`book.yaml` восьми проектов `gu-zhenren/*`), а `extractXHTML` достижим только из `ingestEPUB`.
> Ни одна существующая книга епаб-путь не проходит. Подробно — §6.
---
## §1 Что построено
| № | Пункт | Где |
|---|---|---|
| 14а-1 | `extractXHTML` на токенайзере `x/net/html` вместо `encoding/xml` | `chunk/ingest.go` |
| 14а-1а | Паритетные помощники: `voidTags`, `rawTextTags`, `tokenTagName`, `checkXHTMLCharset`, `unwrapCDATA`, `flushRuby` | там же |
| 14а-2 | `Chapter.EntryName` + два теста (percent-href, `#fragment`) | `chunk/chunktest/epub.go`, `chunk/ingest_test.go` |
| 14а-3 | `mimetype` фикстуры через `zip.Store` + тест-пин OCF | `chunk/chunktest/epub.go`, `chunk/ingest_test.go` |
| 14б-1 | Вырезание `` ДО сплита по `---USER---` | `pipeline/render.go` (`stripPromptComments`) |
| 14б-2 | SHA по канонической (очищенной) форме | там же, `LoadPromptTemplate` |
| 14б-3 | Шапка `terminologist.md` ужата 12 строк → 2 | `prompts/zh-ru/terminologist.md` |
| 14б-4 | Тест-пины + мутации | `pipeline/promptcomments_test.go` |
Диффа: 5 отслеживаемых файлов +651/−97 (из них `ingest.go` +216/−70, `ingest_test.go` +374) плюс новый
`promptcomments_test.go` (161 строка).
---
## §2 14а — переезд на токенайзер
### 2.1 Четыре класса: КРАСНЫЕ до, ЗЕЛЁНЫЕ после
Приёмка «красное-до» снята не рассуждением, а **исполнением оригинальной функции**: старый
`extractXHTML` извлечён дословно из `git show HEAD:backend/internal/chunk/ingest.go` и прогнан по тем же
фикстурам рядом с новым. Старый на всех четырёх — жёсткая ошибка и **пустой текст**, то есть в
`ingestEPUB` это `return nil, err`: один грязный документ убивал импорт всей книги.
| Класс | Старый `encoding/xml` | Новый токенайзер |
|---|---|---|
| голый `<` в прозе | `XML syntax error on line 3: expected element name after <` | `"Если a < b, то дальше.\n\n\n\nВторой абзац."` |
| перехлёст тегов | `XML syntax error on line 3: unexpected end element ` | `"жирный оба курсив хвост."` |
| содержимое `