Name the order that breaks the form-feed cycle, ratify the five-unit inventory that closes a live drift, and pin the chapter count the label alone never caught

This commit is contained in:
heaven 2026-09-05 21:41:58 +03:00
parent 432097193d
commit 24f02bb774

View file

@ -39,7 +39,7 @@
**Поле не переделывать; чинить словарь и формулировку.**
- ⛔ **Валидатор V2-7 «формат провёл ВСЕ границы» (`ingest.go:170-176`) — ЗАМЕЩАЕТСЯ конкуренцией
(§2.4), а не сохраняется рядом с ней.** После конкуренции его предикат
(`len(parts)>1 && len(chapters)==len(parts)`, `:177`) становится мёртвым кодом. Не пытайся
(`len(parts)>1 && len(chapters)==len(parts)`, `ingest.go:178`; ⚠-блок разбора — `:172-177`) становится мёртвым кодом. Не пытайся
сохранить обе логики — это прямо противоречащие друг другу правила.
- **Доминирующий юнит с полом ≥2** для CJK уже есть (`detectChapterUnit`, `ingest.go:249-263`) —
это мини-индуктор, а не список паттернов. **Пол ≥2 — единственный валидатор, живущий в паке 1.**
@ -89,6 +89,21 @@ workdir платформы, **гейту §3 не видна и в него не
- `marker`/`units`/выбор читателя номеров/грамматика шаблона → **файл структуры ИСТОЧНИКА**, свой
загрузчик по `Book.SourceLang`, **по образцу `reader.txt`**.
- ⛔ **В пар-пак класть НЕЛЬЗЯ:** книга без каталога пары идёт с nil-паком (`runner.go:397-407`).
- ⛔ **ОДИН ИСТОЧНИК НА ФАКТ — и дрейф здесь ЖИВОЙ, а не гипотетический.** Замерено 05.09:
`cjk-section.txt` (embedded, читает ИНГЕСТ) несёт **пять** юнитов `章节節回話`, а `zh-ru/heading.txt`
(пар-пак, читает ЧАНКЕР) — **четыре** `章节節回`; `parseHeading` кладёт `units` дословно и на
`CJKSection` не смотрит. ⇒ комментарий `ingest.go:184-185` («the SAME data … ONE source, no
ingest↔chunker byte-drift») **сам протух** — класс строки 299, поправить вместе с кодом.
**Пак не предотвращает возврат дрейфа, а ЗАКРЫВАЕТ живой**, и инвентарь — РЕШЕНИЕ, а не переезд.
**Ратифицировано: инвентарь — ПЯТЬ юнитов, источник `cjk-section.txt`.** Довод: пятый (話) добавлен
`D39.60` §3.1 и на нём стоит пин ja-детекции; ингест уже режет по пяти, отстал именно чанкер, и
сведение к пяти НЕ трогает разрез — меняется только `stripHeading`.
**И это обязано быть ЗАМЕРЕНО шагом 0:** встречается ли `話` шапкой в 蛊真人. Встречается —
меняются чанки и контент-хеши, и это идёт в ветку (б) гейта §3, а не проходит молча.
- ⛔ **Инвентарь скрипт-дефолта CJK берётся ИЗ `cjk-section.txt`, НИКОГДА из пар-файла.** У `ja`
каталога пары нет вовсе (`configs/langpacks/` — только `ru/`, `zh/`, `zh-ru/`), значит ja-книга
идёт по лестнице на скрипт-дефолт. Соберёшь дефолт из опустошаемого `zh-ru/heading.txt` — потеряешь
`話`, и `TestChapterUnitJaWebnovel` (`chunker_heading_test.go:73`) покраснеет НЕ заказанным.
- ⛔ **ОДИН ИСТОЧНИК НА ФАКТ.** Ингест И `stripHeading` чанкера читают **тот же самый файл**.
Сегодня чанкер берёт `HeadingRule` пары; после разведения `heading.txt` оставляет ТОЛЬКО
`template`. Оставить marker в обоих местах = вернуть дрейф ingest↔chunker, который pair-14 закрыл
@ -178,8 +193,19 @@ and the read-only path so status/export/report reproduce the identical snapshot
**И ГЛАВНОЕ — ЧТО С БАЙТАМИ ПРОИГНОРИРОВАННОГО `\f`.** `text.NormalizeSource` form feed **не
трогает** (`ingest.go:44-46`). Сегодня он до провода не доходит, потому что он и есть символ разреза;
после конкуренции проигнорированный `\f` **останется в тексте главы** и уедет модели, в контент-хеш
и в чекеры. ⇒ **игнорируемый `\f` заменяется на разрыв абзаца (`\n\n`) в ингесте, до нормализации**,
и считается.
и в чекеры. ⇒ **игнорируемый `\f` заменяется на разрыв абзаца (`\n\n`) в ингесте, до нормализации**, и считается.
⛔ **ПОРЯДОК ОПЕРАЦИЙ — НАЗВАН ЯВНО, потому что наивный даёт ЦИКЛ и МОЛЧАЛИВУЮ ПОТЕРЮ ГЛАВЫ.**
Решить «есть ли шапки» до замены НЕЛЬЗЯ: перед `\f` нет `\n`, поэтому шапка сразу за ним лежит на
ОДНОЙ СТРОКЕ с прозой предыдущей главы — её отвергают сразу два предиката (длина строки против
`chapterHeaderMaxRunes`, и якорь `^` регэкспа). Заменить безусловно ДО детекции тоже нельзя —
уничтожается разделитель, нужный ветке отката. **Форма:**
**(1)** `\f``\n\n` на РАБОЧЕЙ КОПИИ; **(2)** детекция шапок идёт ПО КОПИИ; **(3)** шапки
победили → режем копию, книга `detected`, форм-фиды сочтены; **(4)** шапки проиграли → режем
**ОРИГИНАЛ** по `\f`, книга `delimited`.
**Существующий тест этого НЕ ловит:** он проверяет только `doc.Structure`, а при наивном порядке
ярлык остаётся верным (`detected`) — **глава теряется молча при зелёном тесте**. Это ровно тот класс,
который пак объявляет своим ⛔ (`D39.205`).
**Слабость выражается СЧЁТОМ, не классом:** `form feeds ignored: N`, `documents attached: M` — в WARN
и в отчёт, **НЕ в манифест**: потребителя на проводе у счёта сегодня нет, а поле без потребителя —
@ -261,11 +287,13 @@ and the read-only path so status/export/report reproduce the identical snapshot
3. **Данные структуры реально двигают `cutTag`** (и через него unit-id) **и в payload `manifestKey` (`manifest.go:336-354`), и НЕ
двигают снапшот волн** — тест на каждое из трёх.
4. **Байты игнорируемого `\f`** — тест, что в тексте главы его не остаётся.
5. **Выравнивание `title_raw` с ПЛОТНОЙ нумерацией** — тест с ПУСТОЙ главой между двумя непустыми:
5. ⛔ **ЧИСЛО ГЛАВ, а не только ярлык, на фикстуре «шапка сразу за `\f`»** (`ingest_test.go:74`):
сегодня тест смотрит только `doc.Structure`, и потерянная глава проходит зелёной. Тест на СЧЁТ.
6. **Выравнивание `title_raw` с ПЛОТНОЙ нумерацией** — тест с ПУСТОЙ главой между двумя непустыми:
пустая глава номер не потребляет, а `title_raw` поедет параллельным срезом, и промах на одну
позицию даст заголовок главы N на главе N+1 **молча**. Тот же класс уже чинили для ruby
(`ingest.go:568-605`, `denseNo`).
6. **Именованный артефакт:** посади дефект в разбор `nav` так, чтобы одна глава потерялась молча,
7. **Именованный артефакт:** посади дефект в разбор `nav` так, чтобы одна глава потерялась молча,
и предъяви **имя покрасневшего теста**. Не краснеет ничто — это находка, а не повод двигаться.
**Каталог мутаций** (`backend/cmd/tmmutate`): `-root` обязан быть КОПИЕЙ; копия вне git ⇒
@ -333,7 +361,8 @@ and the read-only path so status/export/report reproduce the identical snapshot
· тест V2-7 «формат провёл ВСЕ границы» — меняются ДВА кейса, не один: «один случайный `\f`
среди шапок» (шапки побеждают, книга `detected`) **и ПЕРВЫЙ кейс `ingest_test.go:68`
«the format drew every boundary» (`ГЛАВА A\fГЛАВА B``StructureDeclared`), который становится
`delimited`**. ⛔ И **само имя** `TestTXTStructureIsDeclaredOnlyWhenTheFormatDrewEVERYBoundary`
`delimited`**. ⚠ А у кейса `ingest_test.go:74` ожидание НЕ меняется (`detected` и до, и после) —
у него меняется **ЧИСЛО ГЛАВ**, которого никто не проверяет: см. §4 п.5. ⛔ И **само имя** `TestTXTStructureIsDeclaredOnlyWhenTheFormatDrewEVERYBoundary`
после конкуренции ни во что не разрешается — **переименовать тем же актом** (класс строки 299,
который этот же промт требует ниже для комментариев);
· `ingest_encoding_test.go:218-227` (GB18030, `第一章…\f第二章…`) — **счёт глав сохранит** (две