textmachine/docs/BACKEND_CHAPTER_STRUCTURE_SESSION_PROMPT.md

43 KiB
Raw Blame History

Промт бэкенд-сессии: ГРАНИЦА ГЛАВЫ — ФАКТ КНИГИ, А НЕ ДОГАДКА ДВИЖКА

Выдан оркестратором очереди №23. Пак $0. Зона записи — backend/. Ты не коммитишь: дерево передаётся оркестратору на лендинг.

Это НЕ пак «починить осколки английской книги». Осколки — симптом, и самый дешёвый из трёх. Предмет пака — кто и на каком основании решает, где начинается глава.


1. Какая проблема и что решит твой результат

Движок переводит книги мультиагентным пайплайном. Пары языков — ДАННЫЕ, не код; ревью-вопрос по умолчанию: «заработает ли пара, которой в репо ещё НЕТ, без правки Go?».

Скоро пойдёт платный холодный прогон двух книг: около 20 глав китайской и равный по токенам срез английской. Слово владельца, под которым выдан этот пак: «цель — не бежать поскорее приступить к холодному прогону, а подготовить бэкенд так, чтоб этот холодный прогон исполнить максимально правильно. Сколько паков и бэклога нам для этого нужно закрыть, не очень важно. Халтурить не надо, подгонять тоже ничего не надо, спиливаем долг из бэкенда, реализуем актуальный, нужный, чистый архитектурно код».

Что сломано, замерено мною по книге прогона (пере-снимешь своим прибором — все числа воспроизводимы $0). Целей навигации 71: шесть ярлыков частей (BOOK ONEBOOK SIX) и 65 ярлыков глав вида Chapter XIX. A Taste of Home. Издательский конвертер разрезал страницу оглавления на отдельные файлы и объявил навигационную цель на каждый осколок ⇒ 62 цели из 71 указывают во ВТОРУЮ половину spine (позиции целей 8…124 при 126 документах, медиана 89), тогда как проза лежит в первой.

Тела целей, разбиение полное и сумма сходится с 71: 60 побуквенно равны своему ярлыку · 7 коротки, но ярлыку НЕ равны — два титула частей (BOOK ONE+DEATHSEEKER, BOOK FOUR+THIS BLACKENED VEIL) и пять осколков оглавления, у которых вторая строка есть имя СЛЕДУЮЩЕЙ части (Chapter XIX. A Taste of Home + Book Three. To Kill a King) · 4 несут прозу (BOOK TWO/THREE/FIVE/SIX, 1482 тыс. знаков).

А настоящих глав в книге — 84, и это первое, что я ставил неверно. Открывает каждую ДВА подряд абзаца одного класса: римское числительное, затем имя прописными, затем проза другим классом. Замер: абзацев, чей текст есть голое римское числительное, — 84 (81 в шести частях и 3 в открывающей секции), все в одном и том же классе; контроль, доказывающий, что прибор читал книгу, — абзацев с классом в ней 11 397. ⚠ И мой первый замер дал 3 вместо 84, потому что регексп требовал class первым атрибутом, а у большинства первым стоит id. Отрицательный замер здесь особенно дёшев и особенно лжив — печатай контрольную величину рядом.

ГЛАВНОЕ, и ради этого пак переписан. Ярлык навигации «Chapter IV. Unforgiven» есть СКЛЕЙКА ровно того, что проза держит двумя абзацами — IV и UNFORGIVEN. ⇒ навигация несёт правильные ИМЕНА и неправильные АДРЕСА; проза несёт правильные АДРЕСА и расщеплённое имя. Ни один источник не лжёт целиком, и ни одного не хватает в одиночку.

Отсюда — чего в этом паке делать НЕЛЬЗЯ, и я сам чуть этого не заказал: нельзя объявить навигацию врущей и выбросить её. Прежняя редакция промта говорила «настоящих глав шесть» и «навигация о заголовках не знает» — оба утверждения ложны, и сессия, получив 84, сверила бы их с «шестью» и решила, что верное правило неверно.

⇒ Задача — не «поймать мусор эвристикой» и не «отвергнуть навигацию», а склеить два источника: АДРЕС берётся у грамматики, ИМЯ — у того, кто его несёт. Твой результат: английская книга режется по своим настоящим главам, каждая получает имя, и достигнуто это ДАННЫМИ книги, а не веткой в Go.


2. Зона записи и git

Пишешь только в backend/. Чужие зоны читать можно, править нельзя. Исключение — своя секция в docs/PROGRESS.md. Ты не коммитишь: дерево передаётся на лендинг. В дереве могут работать другие сессии — их незакоммиченные файлы не трогай.

НИКОГДА не читай .env и .env.* — там боевые ключи.


3. Карта чтения — пять позиций, и она ЗАКОН

Код-факты добываешь САМА: адреса ниже — отправные точки, а не истина.

  1. CLAUDE.md — канон, цели, гардрейлы.
  2. backend/docs/CHAPTER_STRUCTURE_DESIGN.md — дизайн-оф-рекорд предмета, 2062 строки. Читай §7.1-бис · §7.2 · §7.3 · §7.4 · §8.1 · §8.2 и шапку. ⚠ §9 и §11 НЕ читай: там тринадцать пинов БОЛЬШОГО перекроя с миграциями и вопросы владельца по нему — §4.5 выносит его из пака, и чтение втянет тебя в предмет, которого тебе не заказывали. Статус документа — «ПРЕДЛОЖЕНИЕ, НЕ РАТИФИЦИРОВАНО»: четыре круга опровержения, сходимости нет; предметная часть устояла, механика исполнения — нет. ⇒ ссылаться на него как на решённое НЕЛЬЗЯ; брать оттуда форму — можно и нужно.
  3. backend/internal/chunk/epubtoc.go — разбор навигации и группировка документов.
  4. backend/internal/lang/structure.go + backend/internal/chunk/ingest.go — грамматика заголовка и её потребители.
  5. docs/BACKLOG.md, ряды 283 · 302 · 303 · 201 · 284.

4. Работа

4.0 КОРЕНЬ: ДВА ПУТИ РЕШАЮТ ГРАНИЦУ ГЛАВЫ ПО-РАЗНОМУ И НЕ СПРАШИВАЮТ ДРУГ ДРУГА

Я ставил задачу дважды и дважды неверно: сперва обошёл предмет срезом, потом искал, какой эвристикой ловить мусор. Верная постановка пришла от владельца одной фразой — «у нас есть статический код, который смотрит, глава ли это и как она названа, там как-то это всё должно склеиваться», — и замер её подтвердил.

Замер, пере-снимешь своим прибором. Диспетчер ингеста получает грамматику источника и отдаёт её только одному из двух путей:

  • backend/internal/chunk/ingest.go:243=func ingestTXT(p, encoding, sourceLang string, st *lang.SourceStructure) — грамматику получает;
  • backend/internal/chunk/ingest.go:678=func ingestEPUB(p string, im *lang.InlineMarkup)не получает вовсе, только инлайновую разметку.

Статический код, который умеет сказать «это глава и вот её имя», построен и работает — на одном пути из двух. На пути EPUB его никто не спрашивает: там границу единолично назначает навигация издателя. Отсюда 71 «глава» вместо 84 настоящих.

А вот пустой детерминированный заголовок сюда НЕ относится, и я едва не свалил его в ту же кучу. Он пуст по ДРУГОЙ причине, и твой пак его не заполнит: шаблон берётся из пар-данных (backend/internal/chunk/chunker.go:131=An absent TEMPLATE), а у английской пары файла нет вовсе — в backend/configs/langpacks/en-ru/ лежат inline-markup.txt и terminology.txt, и всё (у zh-ru heading.txt есть). ⇒ либо назови «детерминированный заголовок для en — не результат этого пака», либо закажи en-ru/heading.txt ДАННЫМИ и скажи, как это трётся о ряды 201 и 284. Чего нельзя — оставить его в причинной цепи: тогда отчёт пообещает то, чего правка не делает.

И ЭТОТ ПАК ЕСТЬ «ПАК 2» ПО РЯДУ 303 — объявляю прямо, потому что без этого ты упрёшься в ратифицированный запрет. D39.224 п.7 велит «ось „обязательность юнита" вне пака 2 не трогать (носитель — строка 303)», а сам ряд 303 называет себя «названным ВХОДОМ в пак 2». ⇒ запрет снимается ИМЕННО ЭТИМ паком, и ось трогать можно. Предмет 303: detectChapterUnit итерирует UnitOrdered и без юнита возвращает 0, поэтому грамматика вида «Chapter 12» (маркер + номер, без юнита) книгу не режет — хотя чанкер такой заголовок СНЯТЬ уже умеет. Форма лечения набросана в дизайне §8.2 (доминанта над ФОРМАМИ заголовка, «без юнита» — последний кандидат); она НЕ ратифицирована, и ты вправе предложить другую с доводом.

А вот «283 и 303 — один шов» было моей натяжкой, и её сними. Это РОДСТВЕННЫЕ, но разные предметы: 303 — текстовый резак ингеста; 283 — БИЛДЕР ВЫДАЧИ (backend/internal/bookfile/), делающий главу из каждого документа spine у читалки человека. Общее у них — «грамматика не участвует там, где решают границу», и этого довода достаточно; выдавать их за одну строку значит обещать закрыть 283, чего этот пак не делает.

4.1 РОВНО ТАК: грамматика участвует на ОБОИХ путях, а навигация остаётся источником ИМЁН

Предмет пака — склеить существующие механизмы, а не построить третий. Грамматика даёт АДРЕС границы, навигация даёт ИМЯ. Ни новой эвристики, ни порога, ни ветки по языку.

РЕШИ САМА форму проводки и то, как два источника сводятся в один ответ. Жёстко задаю четыре правила, и каждое — следствие замера, а не вкус:

(а) ДОКУМЕНТ, ИСЧЕРПАННЫЙ СВОИМ ЯРЛЫКОМ, — НЕ ГЛАВА, НО ИМЯ ОН НЕСЁТ. Это тождество, а не порог: тело, равное ярлыку, прозы не содержит по построению. 60 из 71. ⚠ И его имя не выбрасывается — оно единственное место, где «IV» и «UNFORGIVEN» уже склеены в «Chapter IV. Unforgiven».

ОСТАТОК НАЗВАН ЧИСЛОМ, И ПРЯТАТЬ ЕГО НЕЛЬЗЯ: строгое тождество снимает 60, а ПЯТЬ осколков переживуту них вторая строка несёт имя следующей части. «Почти-тождество» я запрещаю: порога не заводить. Решай по роду — либо правило формулируется так, что ловит и эти пять (например «каждая строка тела есть ярлык навигации»), либо остаток НАЗЫВАЕТСЯ числом и принимается осознанно. Чего нельзя — сдать «сделано» и получить 89 глав вместо 84.

(б) ТАМ, ГДЕ НАВИГАЦИЯ НЕ ПОСТАВИЛА ЦЕЛИ, ГРАНИЦУ СТАВИТ ГРАММАТИКА. Настоящие начала глав живут внутри прозы и навигационных якорей не имеют вовсе; сегодня их не видит никто. ⚠ Формулировать это как «навигация молчит» — ошибка: она называет 65 глав поимённо, она лишь целит не туда.

(в) ИМЯ БЕРЁТСЯ У ТОГО, КТО ЕГО НЕСЁТ. Грамматика, взятая одна, даст title_raw = "IV" — то есть ХУЖЕ сегодняшнего, потому что имя в прозе стоит СЛЕДУЮЩИМ абзацем. ⇒ сшивка обязана назвать, откуда каждая из 84 глав получает имя, и это входит в приёмку. Вход ряда 284 и §7.1-бис дизайна (title_raw обязан заполняться любым новым детектором) — читай их до стройки.

(г) ТИТУЛ ЧАСТИ ОБЯЗАН ОСТАТЬСЯ ГРАНИЦЕЙ, И СЕГОДНЯ ЕГО СПАСАЕТ СЛУЧАЙНОСТЬ. Два титула уцелели лишь потому, что несут подзаголовок (BOOK ONE+DEATHSEEKER). Титул без подзаголовка был бы тождеством, и правило (а) сняло бы границу ЦЕЛОЙ ЧАСТИ — молча, при зелёной батарее. Для «пары, которой в репо ещё нет» это класс, а не частность. ⇒ пин ровно на эту форму: тождественный титул, за которым идут документы без навигационных целей, ⇒ граница СОХРАНЯЕТСЯ.

ГРАНИЦЫ, КОТОРЫЕ Я СТАВЛЮ ЖЁСТКО: порога в знаках в Go не заводить · список служебных ролей не расширять (узость намеренна: backend/internal/chunk/epubtoc.go:327=func isServiceRole(role string) bool) · ветки по паре или языку не появляется ни одной.

И ОДНО, ЧТО ПРОМТ ОБЯЗАН БЫЛ РЕШИТЬ И НЕ РЕШАЛ — ИСХОД ДОКУМЕНТА, НА КОТОРОМ ГЛАВУ НЕ ОТКРЫЛИ. Прежняя редакция писала «не потерять ни знака» и тут же требовала переписать докблок backend/internal/chunk/epubtoc.go:289=NO DOCUMENT IS DROPPED HERE — это ДВА РАЗНЫХ исхода, и я их склеил. Замер: группировка приклеивает любой неграничный документ к ПРЕДЫДУЩЕЙ группе, а осколки стоят ПОСЛЕ всей прозы ⇒ «просто не открывать главу» отправит 65 строк оглавления в хвост последней части, и они будут переведены и отгружены читателю — та же трата, что в ряду 283, только в другой главе. ⇒ РОВНО ТАК: документ-тождество ИСКЛЮЧАЕТСЯ из чтения, а не приклеивается, с названной причиной и размером; инвариант сохранения получает это третье состояние; докблок переписывается тем же движением и называет условие, при котором вывод держится. ⚠ Судьбу документа, у которого нет навигационной цели и который сегодня приклеивается к последней части, назови отдельной строкой отчёта.

РОВНО ТАК по видимости: рядом с существующими счётчиками аномалий (unresolved, collapsed, attached) появляется свой, и число доезжает до человека.

4.2 ГРАММАТИКА КОНКРЕТНОЙ КНИГИ ПРИЕЗЖАЕТ ЕЁ ДАННЫМИ

Сегодня грамматика читается только из пар-данных языка (backend/internal/lang/structure.go:142=func LoadSourceStructure(root, sourceLang string)), лестница из трёх ступеней, книжной ступени нет. А грамматика заголовков КОНКРЕТНОГО файла есть свойство конверсии издателя, то есть книги, а не письменности: тот же роман у другого издателя размечен иначе.

И вот здесь я ошибся, проверь сама и не повтори за мной. Я написал было «дом уже ратифицирован — книжный оверлей» (backend/internal/config/book.go:105=LangpackExtend string, backend/internal/lang/langpack.go:413=func LoadWithOverlay(root, src, tgt, overlayRoot string)). Это переоценка: существующий оверлей такой файл ОТКАЖЕТ. Он сливает только файлы манифеста, и его собственная ошибка это говорит — backend/internal/lang/langpack.go:463=an overlay merges only the source/pair manifest files; structure.txt не входит ни в backend/internal/lang/langpack.go:283=var srcFiles = []string{, ни в backend/internal/lang/langpack.go:290=var pairFiles = []string{"palladius.txt". Ратифицирован он для частного канона, не для грамматики.

РОВНО ТАК по дому: книжная ступень у ЛЕСТНИЦЫ ГРАММАТИКИ (backend/internal/lang/structure.go:142=func LoadSourceStructure(root, sourceLang string) — сегодня три ступени, книжной нет), со СВОИМ отпечатком через backend/internal/lang/structure.go:127=func structureFingerprint(origin string, b []byte) string. НЕ через LoadWithOverlay и НЕ полем брифа. Довод денежный: поле брифа уходит в хеш брифа, оттуда в КАЖДЫЙ хеш запроса, и вдобавок рендерится модели. Грамматике границ ехать туда незачем — ей место в КРОЕ, а крой уже фолдит отпечаток структуры.

РЕШИ САМА, что грамматика обязана уметь для этой книги. Приоры, каждый опровергается замером: безмаркерный заголовок (сегодня маркер требуется) · класс римских числительных (сегодня арабские и CJK) · класс римских числительных (сегодня арабские и CJK) · «абзац, чей текст есть голое числительное» · «имя главы — СЛЕДУЮЩИЙ абзац того же класса».

Приор «центрированный абзац» я СНИМАЮ, и вот почему — иначе он столкнул бы тебя лбом с §4.5. Вычисленный стиль виден только внутри обходчика backend/internal/chunk/ingest.go:1155=func extractXHTML, а §4.5 запрещает его трогать: там четыре пина байт-паритета и ошибка ТИХАЯ. Наружу сегодня выходит курсив — глифом, — а канала «центрирован» нет вовсе. ⇒ либо обойдись без стиля (замерь, даёт ли «абзац = голое римское числительное» ровно 84 на ЧИСТОМ тексте и сколько даёт ложных), либо приходи с доводом и ПИНОМ, а не правь обходчик молча. ⚠ Мой приор был внутренне противоречив; это моя ошибка, не твоя развилка.

4.2-бис ПРАВИЛО ЗАГОЛОВКА УЖЕ ПРОТИВОРЕЧИТ САМО СЕБЕ — ряд 346 половина (б), и ты встанешь ровно на него

Найдено оркестратором 18.09 чтением бэклога, в пак внесено намеренно. Ты собираешься заставить грамматику участвовать там, где режут. Но правило «что такое заголовок» сегодня разъехалось по двум предикатам, и они дают РАЗНЫЕ ответы на одном входе. Строить поверх противоречия — значит закрепить его в третьем месте.

Замер, уже снятый и пере-снятый на копии дерева (ряд 346, повторишь своим прибором):

  • Страж ДЛИНЫ живёт только в ингесте — backend/internal/chunk/ingest.go:341=const chapterHeaderMaxRunes = 60; в чанкере длина не проверяется вовсе.
  • Осмысленность НОМЕРА живёт только в чанкере — backend/internal/chunk/chunker.go:315=v := section + num.
  • ⇒ на заголовке С СЕПАРАТОРОМ после юнита (第四章:…) длиной 6184 руны ингест говорит false, чанкер — ok=true. ⚠ Без сепаратора расхождения НЕТ ни на одной длине, и без этой оговорки строка читается как ложная: такую строку отвергают оба предиката.
  • Отдельно: 第零章 (пролог нулевой главой) ингест режет главой, а чанкер заголовок НЕ снимает ⇒ heading="", и сырой CJK-маркер уезжает МОДЕЛИ.

Половина (а) уже закрыта актом D39.225: пин расхождения и паритет-тест стоят, оба под мутациями. Пин написан так, что краснеет ровно тогда, когда приземлится фикс — это сделано намеренно, чтобы фиксер не смог починить молча. ⇒ ты — тот фиксер, и покрасневший пин здесь НЕ «протухший тест», а приглашение; правишь его вместе с правилом и объявляешь это в отчёте по норме «правка, вызванная заказанной сменой поведения».

И вот зачем это в ЭТОМ паке, а не отдельным: предмет один — «чем определяется, что строка есть заголовок главы». Пока он живёт в двух предикатах, «подключить грамматику к EPUB» означает выбрать, к КОТОРОМУ из двух подключаться, и любой выбор молча объявит второй неправым. ⇒ сведи правило в ОДНО место и позови оттуда всех троих — ингест текстовый, ингест EPUB, чанкер. Это и есть «чистый архитектурный код» в формулировке владельца, а не рефактор ради рефактора.

Ряд 346 пере-проверь сама: его прошлая редакция уже была однажды исправлена как ложная, и оговорка про сепаратор — след той правки.

4.3 ЧТО ПРОИСХОДИТ, КОГДА ПОЛИТИКА НЕ ОБЪЯВЛЕНА: ПРОХОД С НАЗВАННОЙ АНОМАЛИЕЙ

РОВНО ТАК, и довод взят из самой кодовой базы: backend/internal/pipeline/runner.go:389=merely costs money it did not mean to spend — конфигурация, которая верна и лишь стоит денег, которых не собиралась тратить, есть форма, о которой этот код ПРЕДУПРЕЖДАЕТ, а не отказывает; отказы — для механизма, который не может работать вовсе. Кривое оглавление — первое, а не второе.

⇒ книга без объявленной грамматики едет как сегодня в части (б), но человек видит числомОговорка, без которой абзац лжёт: правило (а) живёт в Go и действует на КАЖДЫЙ EPUB с осколками-тождествами, объявлена грамматика или нет. Без грамматики отсутствует только (б). ⇒ человек видит числом, что получил: сколько глав, сколько из них несут прозу, какова медиана и самая длинная, сколько фрагментов оглавления опознано тождеством. Одной строкой в человеческой сводке и теми же числами в машинной.

Почему не отказ: большинство реальных EPUB собраны такими же конвертерами. Отказ на «оглавление выглядит странно» сделал бы движок капризным к массовой форме входа и заставил бы объявлять грамматику для каждой книги. Это не «приличная мультиязычность», а её противоположность.

4.4 ЗАМЕР ПЕРЕД СТРОЙКОЙ — ПЕРВЫМ ДЕЛОМ, И БЕЗ ЕДИНОЙ СТРОКИ Go

Прежде чем править код, реализуй правило грамматики на python и прогони по книге прогона. Ответь числом: сколько глав даёт «центрированный короткий абзац + римская цифра + строка капсом» против записей навигации.

это и есть приёмочное число пака. Если правило даёт число, не похожее на настоящую структуру книги, — стройка начинается не с того, и лучше это узнать за ноль долларов и без правок дерева. Так же вчера был пере-снят механизм группировки, и это сэкономило пак.

4.4-бис ЧТО ЭТОТ ПАК СДВИНЕТ В ДЕНЬГАХ — сказано ЗАРАНЕЕ, потому что узнать это после лендинга дорого

Правка правила ингеста двигает ключ манифеста, и не только у английской книги. Конвенция записана в самом коде: backend/internal/pipeline/manifest.go:355=treats an ingest-rule change as a chunkerVersion bump. А в ключ сворачиваются обаbackend/internal/pipeline/manifest.go:281=Chunker: chunkerVersion и backend/internal/pipeline/manifest.go:283=Structure: r.structure.Fingerprint(), причём отпечаток несёт версию алгоритма ДАЖЕ у пустой грамматики (backend/internal/lang/structure.go:104=structureAlgoVersion + "-none"). ⇒ ключ манифеста КИТАЙСКОЙ книги сдвинется тоже. Вдобавок §4.2-бис меняет ТЕКСТ чанка.

И потому: акт D39.269 п. B1 утверждал «китайская половина неподвижна» — это было верно про ПРЕДЫДУЩИЙ пак и перестаёт быть верным про твой. Не «обходи», а НАЗОВИ: в отчёте отдельной строкой — что сдвинулось, у каких книг, и какой пере-снапшот это требует. Эррату к акту пишет оркестратор, не ты. ⚠ И ещё одно следствие, за пределы твоей зоны: пак холодного прогона (docs/COLDRUN_TWO_BOOKS_SESSION_PROMPT.md) несёт ПРЕ-РЕГИСТРИРОВАННЫЕ числа среза английской книги и стоп-правило «срез не совпал с пре-регистрированным → СТОП». После тебя крой другой, и те числа протухают. Твой пак лендится ДО фриза прогона; пере-регистрацию делает пак прогона. Тебе — только строка в отчёте.

4.5 ЧТО В ЭТОТ ПАК НЕ ВХОДИТ — чтобы ты не расширяла его по своей инициативе

Ряд 302 (несколько глав в ОДНОМ документе, разделённых якорями, схлопываются в одну) — противоположный предмет, и дизайн вынес его из окна с названной ценой: единственное место, трогающее байтовый путь извлечения, где ошибка ТИХАЯ. Не трогай. Ряд 201 (движковое «Глава N» доезжает читателю внутри текста) и 284 (название книги и заголовки через термины) — соседние предметы того же дизайна; в этот пак не входят. Увидишь, что твоя правка их задевает, — скажи, не чини. Большой перекрой из дизайна (адресация главы контентом, четыре таблицы по ординалу) — НЕ этот пак. Его механика в четырёх кругах не сошлась, и заходить в неё сейчас значит открыть предмет, который мы не закроем до прогона.


5. Самопроверка исполнением — и где у ЭТОГО пака мягко

Ревью исполнением обязательно: своего кода, своих проб, своих результатов. Субагенты разрешены. Fable 5 — ОДИН советчик с постоянным контекстом; он советчик, а не источник истины.

ЧЕТЫРЕ МЕСТА, ГДЕ ЭТОТ ПАК МЯГОК ИМЕННО ПОТОМУ, ЧТО ОН ТАКОЙ:

  1. Твоя правка меняет КРОЙ, а крой versionируется. По конвенции проекта правка правила нарезки бампает версию нарезки, а та фолдится БЕЗУСЛОВНО — то есть у КАЖДОЙ книги, включая китайскую. Сегодня это бесплатно (книг продуктового пути с оплаченными чанками нет), и именно поэтому пак идёт ДО прогона, а не после. Предъяви цену явно, не умолчи её.
  2. Тождество легко сделать почти-тождеством, и тогда оно станет порогом. «Равно ярлыку» и «начинается с ярлыка плюс немного» — разные правила: первое доказуемо, второе — эвристика с невидимой границей. Замер: 60 тождеств и ОДНО почти-тождество (титульная страница части). Если возьмёшь «почти», скажи, где теперь граница и чем она обоснована.
  3. Грамматика, которая режет ЭТУ книгу, легко окажется грамматикой этой книги. Признак «римская цифра в центрированном абзаце» обязан быть выведен из свойства ВЁРСТКИ, а не подобран под Кристоффа. Контрольный набор я назвал неверно, и неверно он звучал правдоподобно: «китайская книга» — это TXT (books/gu-zhenren), и EPUB-пути она не касается вовсе. Настоящий контроль — два EPUB дерева: books/isekai_majutsushi_jp.epub (CJK-EPUB, получает умолчательную грамматику на EPUB-пути) и books/fifty_shades_1_en.epub (латиница, цели навигации 1:1 с документами — отрицательный контроль: правило (а) обязано на нём не сработать ни разу). Китайская книга — контроль только на сдвиг версии.
  4. Пин на новую грамматику легко построить на той же фикстуре, из которой она выведена. Тогда он докажет, что правило работает на том, из чего сделано. Образец правильной формы в дереве есть — пин, который режет книгу ИЗ ОДНОГО ФАЙЛА ДАННЫХ и падает без него.

6. Оси ревью

  • Код: батарея + живые пробы + СВОИ адверсариальные посадки вне твоего списка. Пак, меняющий крой, принимается прогоном через настоящий драйвер, а не чтением диффа.
  • Данные: каждое значение грамматики — с доводом, из какого свойства вёрстки выведено.
  • Швы: отпечаток структуры едет в крой; проверь, что он двигается ровно тогда, когда должен.

Кодовому паку дополнительно: пин на УСЛОВНОЕ сообщение обязан иметь фикстуру, где оно ОБЯЗАНО прозвучать · перед отчётом сверь каждый клейм с результатом инструмента ЭТОЙ сессии · последний абзац отчёта оказался планом — сделай сейчас · править тесты ради зелени недопустимо, но правка, вызванная ЗАКАЗАННОЙ сменой поведения, есть обслуживание и ОБЪЯВЛЯЕТСЯ · дифф ^func Test предъявляй исполнением.


7. Записка-план

До первой правки кода — записка: что делаешь, в каком порядке, что считаешь спорным. ⚠ Замер §4.4 идёт ПЕРВЫМ и входит в записку числом, а не обещанием.


8. Заявление есть команда

Каждое число — с командой. У каждого НУЛЯ печатается контрольная величина. Ловушки среды: обёртка grep чтит игнор-файлы и не видит books/ — бери command grep · find здесь bfs: относительное время только -mmin, и он тоже чтит игнор-файлы · командной утилиты для баз нет · ⚠ знаки и байты — разные популяции, «текст файла» и «текст тела» — тоже: на этом я сегодня получил ноль тождеств там, где их шестьдесят.


9. Эхо старта

До работы — ≤10 строк СВОИМИ словами: скоуп · инварианты · чего не делаешь. Шли первым действием.


10. Что НЕ удалось и где прибор слеп

Обязательная секция, две половины: «что не получилось» и «что ты знаешь и не сказала» — что невидимо · почему не чинила · чем закрывается.


11. Канал вопросов и право отказаться

Конфликт промта с деревом — пинг, а не интерпретация. Право сказать «этого делать не надо» с аргументом есть и поощряется. ⚠ Этот промт написан после того, как я сам ошибся в постановке дважды: сперва обошёл предмет срезом, потом склеил два факта в один. Оба раза поправил замер, а не рассуждение. Найдёшь третью — говори.


12. Прямой канал

/tmp/textmachine-channel — впиши свой блок первым делом. ⚠ Адрес из файла не доказывает, что сессия жива: сверься с ListAgents.


13. Когда пак считается завершённым

  • Замер §4.4 предъявлен ЧИСЛОМ до первой правки Go.
  • У каждого пункта §4 — исход: сделано · не делаю с доводом · пинг.
  • Английская книга режется по своим настоящим главам, и это предъявлено прогоном, а не рассуждением.
  • Китайская книга не изменилась — предъявлено хешем.
  • Ни одной ветки по языку или паре не добавлено — предъявлено грепом с контрольной величиной.
  • Круги СОШЛИСЬ; прежние находки закрыты таблицей «находка → что сделано → чем предъявлено».
  • Таблица мутаций полная, ТРИ исхода: поймано · выжило · НЕ ИЗМЕРЕНО; новые записи введены в гейт.
  • Числа сняты ПОСЛЕ последней правки; гейт прогнан целиком; рядом с ok напечатан счёт скипов.
  • Явное «работа завершена, править не планирую».

И последнее. Предмет пака — кто решает, где начинается глава. Ответ, к которому надо прийти: тождество решает движок, потому что это факт, а не догадка; АДРЕС границы решает грамматика книги; ИМЯ берётся у того, кто его несёт. Всё, что сверх этого, решают данные книги. Если твоё решение где-то ставит догадку движка на место данных — оно не готово, сколько бы тестов ни было зелёными.