52 KiB
ПАК «ФОРМАТЫ И ЧЕСТНЫЙ СЛОВАРЬ» — движковая сессия
ВЫДАН 05.09 сессии
textmachine-main-fe. Три круга совместного ревью (оркестратор + Fable 5); состав пака и словарь — итог восьми кругов сходимости, тела разборов вD39.202/D39.207.
Зона записи: backend/. Коммитит оркестратор (textmachine-main-27), ты готовишь дерево и
передаёшь. Отчёт: секция «Бэкенд» в docs/PROGRESS.md. Денег: $0, ни одного платного вызова.
0. КАРТА ЧТЕНИЯ — ПЯТЬ ПОЗИЦИЙ, ОНА ЗАКОН
Дальше — только по её ссылкам. Голову журнала решений целиком НЕ читать (канон это запрещает).
backend/internal/chunk/ingest.goцеликом — предмет пака живёт здесь.docs/research/27-chapter-detection.md— через его ревью-шапку. Дизайн НЕ ратифицирован; это материал, а не заказ.## D39.205(пропущенный кусок — критическая поломка) и в## D39.202— только п.3-бис (гейт умирает вытеснением) и п.9 (чего не сторожит ничто). Тело ноты целиком не нужно. ⚠ ПОДНОМЕР своего тела не имеет —D2.3живёт пунктом внутри^## D2.- Строки бэклога 297 (словарь провенанса) и 299 (имя, не разрешающееся ни во что) в
docs/PROGRESS.md. 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),ingest.go:178; ⚠-блок разбора —:172-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
Без него пак не имеет права ложиться: под ним подписанный банк владельца (окна в ординалах глав) и боевая книга.
- ⛔ Пин — ПОЛНЫЙ МАНИФЕСТ до правок, а не хеш: 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: пин снялся бы с другого разреза, и сравнивать было бы нечего. ⛔ И знай, КУДА они указывают: у ДВАДЦАТИ конфигов из 31langpack_rootсмотрит в СОСЕДНЕЕ РАБОЧЕЕ ДЕРЕВО, а оно на ДРУГОЙ ВЕТКЕ (/home/ubuntu/projects/textmachine, веткаpolygon); ещё девять — в твоё (main), два не объявляют его вовсе. §7 разрешает ЧТЕНИЕ соседнего дерева по этим путям. Перенаправлять нельзя (см. выше), писать туда нельзя. ⚠ Прежняя редакция обосновывала это тем, что «деревья различаются,ru/есть только там» — ЛОЖНО и снято: замерено 05.09, каталогиconfigs/langpacksв обоих деревьях байт-в-байт идентичны (diff -rqпуст, sha256heading.txtсовпадают). Комментарии в конфигах стенда, утверждающие обратное, ПРОТУХЛИ — класс строки 299; править их не твоя зона, но и верить им нельзя. Чтение разрешено по-прежнему, но не по этому доводу. - Синтетические фикстуры по стандартам: EPUB 3 с
nav, EPUB 2 с NCX. Сегодня тестовый билдер пишет толькоcontainer→OPF→spine→xhtmlи nav/NCX не пишет вовсе (chunktest/epub.go:42-82). - Оракул — КОД, не замер: EPUB с
navразворачивается в txt, путь разреза обязан восстановить nav-границы. Ground truth без ручной разметки и без единого вызова модели. ⚠ Сам ЗАМЕР — пак 2. - Замеры ДО правок, каждый числом в отчёт: есть ли
\fв стенд-книгах · есть ли главы-огрызки от print-TOC · сбрасывается ли нумерация по томам.
Файлы стенда — поимённо (см. §7 о доступе): 蛊真人 (GB18030 txt, ровно 2283 «第N节») · EPUB
владельца Кристофф/en · EPUB владельца isekai/ja.
⛔ ПРО НОСИТЕЛЬ ГЕЙТА, иначе пин повиснет в воздухе: конфигов book.yaml для 蛊真人 в дереве книг
ДЕСЯТОК, и ни один не указывает на полную книгу — все на срезы (slice25, ch1-10, ch1-5,
ch4-5). Полный guzhenren-gb18030.txt не привязан ни к одному конфигу.
⇒ Гейт §3 = каждый РАЗЛИЧНЫЙ ИСХОДНИК воспроизводит свой разрез глав. Их ДЕВЯТЬ, а конфигов 31.
Это не сужение, а верный предикат: разрез ГЛАВ есть функция байтов исходника, кодировки и данных
структуры — от пар-пака он не зависит (шапки ищутся по embedded cjk-section.txt, не по паре).
Конфиги, делящие исходник, дают тот же разрез по построению, и тридцать один пин мерил бы одно и то
же девять раз. ⚠ Тридцать один понадобился бы только для сравнения unit-id — а их из гейта вывела
находка 4.
Полную книгу (2283 главы) гоняешь ОТДЕЛЬНО, своим конфигом в своём каталоге; её результат идёт числом
в отчёт и гейтом НЕ является — базовой линии для неё не существует. ⚠ Платная книга платформы bk_SS5VES2JELESJSTR живёт в
workdir платформы, гейту §3 не видна и в него не входит; её заметки теряются по слову оркестратора.
2.2 Данные структуры — ПО ИСТОЧНИКУ, вне пар-пака
Ревью-вопрос проекта: «заработает ли пара, которой в репо НЕТ, без правки Go?»
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 закрыл («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) подвинется САМ — он идёт по полям рефлексией. ⚠ Ручной список живёт в соседнем
TestTheCutTagActuallyCarriesTheEngineData (cuttag_test.go:54-61) — новый компонент туда сам
не попадёт, добавляй руками.
ФОРМА компонента — не на твоё усмотрение, она уже задана прецедентом:
Structure: <версия парсера> + sha256(байты структурных данных, РЕАЛЬНО загруженных для этой книги — /structure.txt либо дефолт по скрипту). Никогда omitempty — дефолт есть всегда. ⚠ И не копируй тег с Langpack
буквально: у него omitempty КАК РАЗ стоит, и в голдене langpack_version отсутствует вовсе. Бери
из его формы ПРИНЦИП — «считается одинаково на пишущем и читающем пути», — а не строку тега.
Считается ОДИНАКОВО на пишущем и читающем пути (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 / грамматика шаблона — и переезжают они из ПАР-ФАЙЛА
(zh-ru/heading.txt), а НЕ из cjk-section.txt.
⛔⛔ internal/lang/data/cjk-section.txt НЕ ПРАВИТСЯ ВООБЩЕ — ни одной строкой. Он уже объявляет
себя единственным источником истины для разреза, и чанкер просто начинает читать ЕГО (через
lang.DefaultCJKSection()) вместо своей отставшей копии — вот этим дрейф и закрывается.
Тронешь файл — сдвинется EmbeddedVersion, а он тянет три вещи, каждая из которых тебя остановит:
голден несёт четыре строки embedded_version наравне с chunker_version;
TestChapterMarkerIsData (langpack_test.go:454) краснеет НЕ заказанным; и — главное —
CURRENT-STATE (docs/PROGRESS.md) держит правило лендинга: правка lang/data/*.txt двигает
EmbeddedVersion → cutTag, и такой лендинг НЕ кладётся, пока жива книга платного прогона.
Оставить там же мёртвые копии ключей тоже нельзя — это класс строки 299.
⛔⛔ ЧТО ДЕЛАТЬ С ПАР-ФАЙЛОМ, КОТОРЫЙ ЖИВЁТ В ЧУЖОЙ ВЕТКЕ. Вынув marker/units из
zh-ru/heading.txt, ты обязан решить, как parseHeading встретит СТАРЫЙ файл — а он останется
лежать в соседнем дереве на ветке polygon, куда тебе писать запрещено. Ратифицировано:
парсер ОТВЕРГАЕТ marker/units с ЯВНЫМ переходным сообщением («moved to source structure data;
remove them from heading.txt»), а не молча их глотает. Терпимость завела бы ровно тот мёртвый ключ,
который запрещён абзацем выше, и превратила бы громкий отказ в тихое расхождение.
⚠ И знай цену, которую это назначает НЕ ТЕБЕ: двадцать конфигов с корнем в polygon сломаются
не при твоём лендинге в main, а в тот момент, когда полигон вольёт main в свою ветку — до
этого он собирается из своего дерева со своим файлом и своим парсером. Это межветочная зависимость
данных, которую никто не проектировал. Твоё дело — назвать её числом в отчёте; предупредить
полигон и завести строку — моё.
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<guide>через<reference type=…>(EPUB 2). ⛔ Обе формы обязательны: landmarks — конструкция EPUB 3, у EPUB 2 nav-документа нет вовсе, и сессия, проверившая только landmarks, приклеитtoc.xhtmlк главе 1 ровно на том «массовом EPUB 2», которым это правило себя и мотивирует. ⚠properties="cover-image"стоит на ИЗОБРАЖЕНИИ, а не на документе, и среди документов spine не исключит ничего; landmarks — это<nav epub:type="landmarks">ВНУТРИ nav-документа, вpropertiesих нет. Грепнуть одинpropertiesи на этом успокоиться — значит приклеить к главе 1 текстовую страницу оглавления (toc.xhtmlв spine — массовый EPUB 2) и титул. Больше НИЧЕГО не исключается; - ⛔
linear="no"НИКОГДА не начинает главу, даже если nav на него указывает (сноски, приложения) — прикрепляется к предыдущему по spine; - непокрытые контент-документы ДО первой цели nav (титул, обложка-страница) прикрепляются к ПЕРВОЙ главе — зеркало правила преамбулы txt;
- остальные непокрытые — к предыдущей главе;
- каждое прикрепление считается; число в WARN и в отчёт.
⚠ Вырожденный nav — ОЖИДАЕМЫЙ исход пака 1, а не твой баг. Скрейп с тремя целями на триста
документов даст три главы-кита с ярлыком declared и огромным счётом прикреплений. Словарь честен,
разрез — нет; лечение (валидаторы runt/whale, кросс-аудит) — пак 2 (§5). Тревога здесь — СЧЁТ,
и это всё, что от пака 1 требуется. Не лезь чинить кита.
2.4 Словарь: четвёртое значение и КОНКУРЕНЦИЯ свидетелей
Словарь: declared · delimited · detected · none.
declared— формат утверждает СТРУКТУРУ ГЛАВ (nav/NCX).delimited(новое) — в файле был РАЗДЕЛИТЕЛЬ, но «это глава» — допущение движка. Сюда уходят и EPUB-spine, и\fв txt: spine объявляет порядок чтения, form feed — разрыв СТРАНИЦЫ. ⚠ Реальный источник\f— не человек, а извлечение из PDF (pdftotextставит его на каждом разрыве страницы по умолчанию).detected— движок вывел границу из СОДЕРЖИМОГО.none— свидетеля нет (и при <2 главах — уже так).
⛔ КОНКУРЕНЦИЯ вместо сложения — заказанная СМЕНА ПОВЕДЕНИЯ. Сегодня txt режется по \f, а
ПОТОМ каждая часть — по шапкам (ingest.go:159-167), пути СКЛАДЫВАЮТСЯ. Заказано: есть шапки →
режем по шапкам, книга detected; шапок нет → режем по \f, книга delimited. Смешанного случая
не остаётся по построению, агрегировать нечего, structureOf сводится к «none при <2 главах,
иначе свидетель победившего пути».
⛔ И ГЛАВНОЕ — ЧТО С БАЙТАМИ ПРОИГНОРИРОВАННОГО \f. text.NormalizeSource form feed не
трогает (ingest.go:44-46). Сегодня он до провода не доходит, потому что он и есть символ разреза;
после конкуренции проигнорированный \f останется в тексте главы и уедет модели, в контент-хеш
и в чекеры. ⇒ игнорируемый \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
и в отчёт, НЕ в манифест: потребителя на проводе у счёта сегодня нет, а поле без потребителя —
механизм ради галочки.
⚠ Провенанс — свойство ГРАНИЦЫ. Глав N, границ N−1; у первой главы открывающей границы нет.
⚠ ПРАВИЛО ПРЕАМБУЛЫ ЗАМОРОЖЕНО: текст до первой шапки сливается в главу 1, и код объясняет
зачем — «dense numbering aligns with the section numbers», «第一节» обязано быть главой 1
(splitTextChapters, ingest.go:265-270). Не трогать: правка сдвинет нумерацию ВСЕЙ стенд-книги.
⚠ Следствие, которое надо знать, а не чинить: у главы 1 шапка стоит НЕ в её начале, значит
title_raw главы 1 берётся из проглоченной шапки.
2.5 title_raw и карта сдвигов
title_rawедет по проводу аддитивно. Определение: строка шапки как в источнике (txt) /navLabelдословно (EPUB). ⛔ Обоснование — собственное: заголовок, не едущий по проводу, невозможно отрендерить. НЕ ссылаться на «шесть заголовков из семнадцати»: у числа нет носителя (эррата 05.09-е).- ⛔ ПОНИЖЕНИЕ подзаголовка в прозу (
chunker.go:155-177) НЕ ТРОГАТЬ. Снимешь — изменятся чанки и модель перестанет переводить подзаголовок.title_rawдобавляется РЯДОМ, не вместо. - ⚠ В EPUB-экспорт и в
navtitle_rawв этом паке НЕ входит — он на языке источника. - ⛔ БАМП
chunkerVersion— ОДИН РАЗ, И ЭТО ЗАКАЗАНО ЯВНО. Пак меняет РАЗРЕЗ (EPUB spine→nav, конкуренция\f), а правило ингеста по конвенции покрываетсяchunkerVersion(text/source.go: «covered by chunkerVersion (a deliberate re-translation, not a silent one)»). Забыть бамп — оставить протухший манифест выглядящим текущим. ⇒chunker_versionуезжает в снапшот, и голден краснеет:testdata/golden/capture.goldenнесёт ЧЕТЫРЕ строкиchunker_version(провереноgrep -c). Голден пере-захватывается ОДИН раз, в самом конце, и ТОЛЬКО после того, как ты предъявишь пин: разрез самого голдена не изменился (его фикстура — ja-txt с第一章, форм-фидов ноль). Объявление по D39.183 обязательно. ⚠ Без этого абзаца ты упёрся бы в красный голден и по §7 обязан был бы остановиться и пинговать — поэтому он здесь: правка ЗАКАЗАНА. - Карта сдвигов — МАШИННАЯ (JSON), set-diff по id и номеру:
same/moved/gone/new. Прозой нельзя: на прозе не построить мигратор потом. Мигратор в пак НЕ входит — пак производит УЛИКУ, а не ремонт, и это сказано вслух.
3. ГЕЙТ ЛЕНДИНГА
Зелено, если выполнено ОДНО из двух:
- (а) РАЗРЕЗ воспроизводится байт-в-байт против пина §2.1 — и предмет сравнения назван точно: id глав и их номера, ЛИБО
- (б) расхождение перечислено, приложена машинная карта сдвигов, в отчёте ЯВНО названа цена: что именно теряется и у кого, и это подписано владельцем.
⛔⛔ ЧТО В СРАВНЕНИЕ НЕ ВХОДИТ, иначе ветка (а) недостижима по построению. key и unit-id
ОБЯЗАНЫ сдвинуться — каждый unit-id склеен как chapterID:cutTag:idx (manifest.go:220-221), а
§2.2 прямо ТРЕБУЕТ, чтобы компонент Structure вошёл в cutInputs и двинул cutTag; шапка того же
кода обещает «ANY re-cut mints new unit ids across the whole book» (:218). ⇒ полный JSON разойдётся
у КАЖДОГО конфига, и разойдётся не на дефекте, а на работе, которую пак сам заказал. Это не
расхождение, а исполнение §2.2, и одноразовая перечеканка id уже учтена отдельной ценой — строка
бэклога 298 прямо говорит, что она «другой предмет и другая цена».
⇒ Сверяешь id глав и номера. key и unit-id — движутся, и это ожидаемо.
⛔ Почему не только (а). Чистое «байт-в-байт» защищало бы ОШИБКИ нынешнего разреза: правильный
детектор чинит главы-огрызки, а гейт бы его за это не пустил. Это девятая форма ложной зелени —
гейт, ЗАЩИЩАЮЩИЙ дефект (D39.202). ⚠ Такого детектора в паке 1 нет (он в §5, пак 2) — довод
объясняет ФОРМУ гейта на будущее, а не заказывает тебе детектор.
⚠ Ветка (б) честна ровно наполовину, и это записано сознательно: мигратора окон банка нет, и пак
его не строит. Сдвиг окна = новый TermID = каскад на решения владельца в платформе. Поэтому (б)
обязывает НАЗВАТЬ потерю, а не починить.
⚠ Поток подписи — не твой. Ты сдаёшь карту и названную цену в отчёте; оркестратор несёт владельцу; лендинг — после его слова. Не жди сам и не «подписывай» сам.
⚠ ГЕЙТ ПРИМЕНЯЕТСЯ ТОЛЬКО К 蛊真人 — под ней подписанный банк владельца (окна в ординалах глав). EPUB владельца под гейт НЕ попадают: их разрез ОБЯЗАН измениться (spine → nav), это и есть заказ пака. Для них ветка (а) провалена по построению, и требовать под них подпись владельца бессмысленно. Они — прибор §2.1 п.3: их новый разрез ИЗМЕРЯЕТСЯ и идёт в отчёт числом, а не гейтится.
✅ ВЕТКА (а) ДЛЯ 蛊真人 ОБЕСПЕЧЕНА ЗАМЕРОМ, а не надеждой. Шаг 0 сессии 05.09: форм-фидов
НОЛЬ во всех шести файлах стенда, значит ветка отката конкуренции не срабатывает нигде и разрез
боевой книги сдвинуть НЕЧЕМ. Там же: 話 в 蛊真人 — ноль вхождений вообще, поэтому сведение
чанкера к пяти юнитам не меняет ни одного её чанка и ни одного контент-хеша; ветка (б) по инвентарю
не нужна. ⚠ Обратная сторона, и она про твою самопроверку: раз \f в реальных книгах нет,
путь конкуренции проверяется ИСКЛЮЧИТЕЛЬНО синтетическими фикстурами — они здесь единственная
улика, и потому именованный артефакт §4 обязан целить именно туда, а не только в nav.
4. САМОПРОВЕРКА — ИСПОЛНЕНИЕМ, И ВЕЕР НАПРАВЛЕН
Самоотчёт «проверено» без исполнения регулярно оказывается ложным. После кода — адверсариальный проход по СВОЕЙ готовой работе. Глубину и веер выбираешь ты; направление — вот что уязвимо здесь:
- ⛔ Резка xhtml по якорям
nav(несколько глав в одном файле) — самое уязвимое место пака. УextractXHTMLне меньше четырёх пинов байт-паритета (ingest_test.go:323, 359, 423, 467: void-теги, несбалансированный ruby, raw-text-элементы, голый амперсанд). Ошибка здесь ТИХАЯ, и ломает она именно их. - Загрузчик структуры обязан достигать книги БЕЗ пар-пака — тест на книгу без
langpack_root. - Данные структуры реально двигают
cutTag(и через него unit-id) и в payloadmanifestKey(manifest.go:336-354), и НЕ двигают снапшот волн — тест на каждое из трёх. - Байты игнорируемого
\f— тест, что в тексте главы его не остаётся. - ⛔ ЧИСЛО ГЛАВ, а не только ярлык, на фикстуре «шапка сразу за
\f» (ingest_test.go:74): сегодня тест смотрит толькоdoc.Structure, и потерянная глава проходит зелёной. Тест на СЧЁТ. - Выравнивание
title_rawс ПЛОТНОЙ нумерацией — тест с ПУСТОЙ главой между двумя непустыми: пустая глава номер не потребляет, аtitle_rawпоедет параллельным срезом, и промах на одну позицию даст заголовок главы N на главе N+1 молча. Тот же класс уже чинили для ruby (ingest.go:568-605,denseNo). - Именованный артефакт — ДВА, и второй важнее: посади дефект в разбор
navтак, чтобы одна глава потерялась молча, и предъяви имя покрасневшего теста. Не краснеет ничто — это находка, а не повод двигаться.
⚠ Каталог мутаций (backend/cmd/tmmutate): -root обязан быть КОПИЕЙ; копия вне git ⇒
GOFLAGS=-buildvcs=false. Не работай под /tmp: посторонний /tmp/.git ломает go build
всему, что там собирается, и уже дал этой смене ложную зелень.
5. ГРАНИЦЫ — ЧЕГО В ЭТОМ ПАКЕ НЕТ
Названо, чтобы не открылось по ходу и не было сделано «по здравому смыслу»:
- Лексиконы не-CJK, корпус, ЗАМЕР оракулом, сбор файлов, сеть — пак 2. Пак 1 ничего не собирает.
- ⛔ Структурные валидаторы (монотонность номеров, коридор hits/объём, headSpread, runt/whale) —
ПАК 2. В паке 1 живёт только уже построенный пол ≥2. Причина не стилистическая: у стенд-книги
нумерация СБРАСЫВАЕТСЯ по томам (
eval/dovodka/narezka.py:18), и валидатор монотонности отверг бы или перекроил её шапки — то есть уронил бы гейт §3 на верной книге. - Индуктор скелетов строк — пак 2, и только если замер покажет нужду.
- Мигратор окон банка,
chunk_status,unit_resolutions— не здесь (строка 298, D15.2). - Идентичность по ИСХОДУ (
cutTagкак дайджест нарезки) — свой пак. Здесь только сравнение манифестов до/после в приборе шага 0. - Публикация провенанса по главе,
kind, вердикт качества, иерархия томов, print-TOC-линковка,split/mergedв карте — не строить. Потребителя нет, фронт заморожен. - FB2 — только если владелец скажет, что у беты есть FB2-источники.
- Платформенное: предикат «двух свидетелей» и
structure_version· снятие временного правила поdeclared(строка 297) · глагол «перечитать журнал» — всё это НЕ твоя зона. Не добавляй полей «для платформы». - ⛔ Рендер при сбросе нумерации по томам — ВОПРОС ВЛАДЕЛЬЦА, не чинить. ⚠ Прежняя редакция
обещала, что ты увидишь это «в
navплатного EPUB» — неверно и снято: платная книга платформы собрана из десятиглавого txt-среза,navтам нет вовсе, и сброса по томам в главах 1–10 быть не может. Увидеть это можно только на ПОЛНОЙ 蛊真人, где нумерация и сбрасывается. Это выбор рендера (номер книги против плотного ординала), он вне пака. Увидел — запиши числом в отчёт и иди дальше. narezka.pyполигона (второй приватный zh-детектор) — пинг полигону от оркестратора, не твоё.- ⛔ Сквозного прогона не-CJK НЕ БУДЕТ и не заказывается: промтов пары для en/ja не существует
(
backend/prompts/— толькоzh-ru). Не-CJK путь проверяется только на уровне ingest/manifest, $0. Не пытайся «доказать бету» — не сможешь, и это не твой дефект.
6. ОБЯЗАТЕЛЬСТВО, КОТОРОЕ ПАК ДАЁТ ПЛАТФОРМЕ
Пак НЕ бампает manifestVersion. Значения и поля аддитивны; платформа игнорирует неизвестные
поля и деградирует на неизвестных значениях. Бамп понадобился бы, только если читатель прочтёт
документ как текущий и НЕВЕРНО: единица без плотного номера или с дубликатом · тотал, не равный
списку · пустой id · смена ФОРМЫ id.
⇒ Граница обязательства: каждая единица манифеста нумерована ПЛОТНО и 1-based. «Фрагмент» —
атрибут нумерованной единицы, а не отсутствие номера.
⚠ Контракт при этом обещает number: null как законный — и шов его отвергает; противоречие
записано эрратой 05.09 в openapi.yaml. Снимать его — не в этом паке.
⚠ Стыковка с платформой — предусловие ЛЕНДИНГА и забота ОРКЕСТРАТОРА, не твоя (platform/ —
чужая зона). От тебя нужно одно: сдать в отчёте манифест-фикстуру со значением delimited,
чтобы платформенный тест мог на ней проверить деградацию в «знаки».
7. ДИСЦИПЛИНА
- Доступ к книгам.
books/— симлинк в соседнее дерево. ЧИТАТЬ файлы подbooks/РАЗРЕШЕНО и необходимо (§2.1 без этого неисполним). ⛔ ЗАПРЕЩЕНО: любая запись подbooks/, любые git-операции там, и любая ЗАПИСЬ в/home/ubuntu/projects/textmachine. ⚠ ЧТЕНИЕ соседнего дерева разрешено там, куда указывают конфиги стенда — прежде всегоbackend/configs/langpacks(langpack_root): без этого §2.1 неисполним, потому что пин обязан сниматься ровно с тем пар-паком, с которым книга резалась. - Тесты и гейты не подгонять под зелень. ⚠ Правка, вызванная заказанной сменой поведения, —
обслуживание. Заказанные здесь правки тестов, поимённо:
·
priceprojection_test.go:144— кейс «a form feed is the format speaking» ждётStructureDeclared; станетdelimited; · тест V2-7 «формат провёл ВСЕ границы» — меняются ДВА кейса, не один: «один случайный\fсреди шапок» (шапки побеждают, книгаdetected) и ПЕРВЫЙ кейсingest_test.go:68«the format drew every boundary» (ГЛАВА A\fГЛАВА B→StructureDeclared), который становитсяdelimited. ⚠ Уточнение, чтобы ты не искал несуществующего: ассерт переезжает РОВНО ОДИН — тест сверяет толькоdoc.Structure; у кейса с одним\fсреди шапок ожидание то же и до, и после, у него меняются объясняющий комментарий и ЧИСЛО глав (§4 п.5). ⚠ А у кейсаingest_test.go:74ожидание НЕ меняется (detectedи до, и после) — у него меняется ЧИСЛО ГЛАВ, которого никто не проверяет: см. §4 п.5. ⛔ И само имяTestTXTStructureIsDeclaredOnlyWhenTheFormatDrewEVERYBoundaryпосле конкуренции ни во что не разрешается — переименовать тем же актом (класс строки 299, который этот же промт требует ниже для комментариев); ·ingest_encoding_test.go:218-227(GB18030,第一章…\f第二章…) — счёт глав сохранит (две шапки побеждают), но его комментарий про разрез по\fстанет ложью — поправить текст комментария, это класс строки 299; · ⚠TestIngestTXTFormFeedChapters(ingest_test.go:34-46) проверяет разрезA\fB\fC, где шапок нет — при конкуренции НЕ меняется, меняется только ярлык наdelimited. Не «чини» его. Каждая такая правка ОБЪЯВЛЯЕТСЯ в отчёте: что изменилось в поведении, какой тест это описывал, куда уехала гарантия. Не заказано и не ратифицировано — пинг, не правка. - Комментарии, описывающие сменённое поведение, — тоже носители. Правь их вместе с кодом; в
частности протухает
bookfile/epub.go:15-19(«ридер делает главу из КАЖДОГО документа spine»). Это класс строки 299 — имя или утверждение, которое больше ни во что не разрешается. - Не хакать вокруг ошибки. Нашёл дефект в чужой зоне — опиши, не чини.
- Никогда не читать
.env. PUML не рендерить. Не коммитить. Не двигать HEAD. Не трогать чужое незакоммиченное. ⚠ Якорный линт доков (docs/scripts/counts.py --lint) — гейт ОРКЕСТРАТОРА; доки с якорями ты не пишешь, но знай, что он есть, и не строй второй такой. - Утверждение о замере — с КОМАНДОЙ (
D39.207п.5): пишешь «проверено» или «не проверено, потому что X» — рядом стоит команда, которой это показано. - ⛔ Идентификатор, не виденный ЦЕЛИКОМ в выводе команды, не воспроизводить по памяти — ни хеш,
ни номер строки. Обрезающий
cut/headмолча превращает улику в догадку (research/29§5.6: так был сфабрикован sha256, совпавший на 52 знака из 64).
8. ЧТО ПРИСЛАТЬ И В КАКОМ ПОРЯДКЕ
- Эхо старта: своими словами — что понял предметом и что считаешь границей. До кода.
- Записка-план в отчёт: что делаешь, в каком порядке, и чем сдача осмысленна, если тебя оборвут на середине. До кода.
- Замеры шага 0 — числами, до правок.
- Код.
- Отчёт: батарея (команда, скипы числом, EXIT) · каталог · посадка именованного артефакта
(
file:line· что подменено · какое ИМЯ теста покраснело) · объявления по D39.183 · счетаform feeds ignored/documents attached· манифест-фикстура сdelimited· что НЕ проверено и почему.
⚠ Споткнулся о невыполнимое, о противоречие в самом задании или о нехватку доступа — пиши сразу, не обходи молча. Обход тихо становится нормой, и следующая смена наследует его как факт.