textmachine/docs/CHAPTER_STRUCTURE_SESSION_PROMPT.md

38 KiB
Raw Blame History

ПАК «ФОРМАТЫ И ЧЕСТНЫЙ СЛОВАРЬ» — движковая сессия

ВЫДАН 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, 666lang.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 <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) в ингесте, до нормализации, и считается.

Слабость выражается СЧЁТОМ, не классом: form feeds ignored: N, documents attached: M — в WARN и в отчёт, НЕ в манифест: потребителя на проводе у счёта сегодня нет, а поле без потребителя — механизм ради галочки.

Провенанс — свойство ГРАНИЦЫ. Глав N, границ N1; у первой главы открывающей границы нет. ⚠ ПРАВИЛО ПРЕАМБУЛЫ ЗАМОРОЖЕНО: текст до первой шапки сливается в главу 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-экспорт и в nav title_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, ЛИБО
  • (б) расхождение перечислено, приложена машинная карта сдвигов, в отчёте ЯВНО названа цена: что именно теряется и у кого, и это подписано владельцем.

Почему не только (а). Чистое «байт-в-байт» защищало бы ОШИБКИ нынешнего разреза: правильный детектор чинит главы-огрызки, а гейт бы его за это не пустил. Это девятая форма ложной зелени — гейт, ЗАЩИЩАЮЩИЙ дефект (D39.202). ⚠ Такого детектора в паке 1 нет (он в §5, пак 2) — довод объясняет ФОРМУ гейта на будущее, а не заказывает тебе детектор.

Ветка (б) честна ровно наполовину, и это записано сознательно: мигратора окон банка нет, и пак его не строит. Сдвиг окна = новый TermID = каскад на решения владельца в платформе. Поэтому (б) обязывает НАЗВАТЬ потерю, а не починить.

Поток подписи — не твой. Ты сдаёшь карту и названную цену в отчёте; оркестратор несёт владельцу; лендинг — после его слова. Не жди сам и не «подписывай» сам.

ГЕЙТ ПРИМЕНЯЕТСЯ ТОЛЬКО К 蛊真人 — под ней подписанный банк владельца (окна в ординалах глав). EPUB владельца под гейт НЕ попадают: их разрез ОБЯЗАН измениться (spine → nav), это и есть заказ пака. Для них ветка (а) провалена по построению, и требовать под них подпись владельца бессмысленно. Они — прибор §2.1 п.3: их новый разрез ИЗМЕРЯЕТСЯ и идёт в отчёт числом, а не гейтится.

Ожидаемый исход для 蛊真人 — ветка (а). В паке 1 её разрез может сдвинуть только \f-конкуренция. Ветка (б) существует на случай, если \f там окажется.


4. САМОПРОВЕРКА — ИСПОЛНЕНИЕМ, И ВЕЕР НАПРАВЛЕН

Самоотчёт «проверено» без исполнения регулярно оказывается ложным. После кода — адверсариальный проход по СВОЕЙ готовой работе. Глубину и веер выбираешь ты; направление — вот что уязвимо здесь:

  1. Резка xhtml по якорям nav (несколько глав в одном файле) — самое уязвимое место пака. У extractXHTML не меньше четырёх пинов байт-паритета (ingest_test.go:323, 359, 423, 467: void-теги, несбалансированный ruby, raw-text-элементы, голый амперсанд). Ошибка здесь ТИХАЯ, и ломает она именно их.
  2. Загрузчик структуры обязан достигать книги БЕЗ пар-пака — тест на книгу без langpack_root.
  3. Данные структуры реально двигают cutTag (и через него unit-id) и в payload manifestKey (manifest.go:336-354), и НЕ двигают снапшот волн — тест на каждое из трёх.
  4. Байты игнорируемого \f — тест, что в тексте главы его не остаётся.
  5. Выравнивание title_raw с ПЛОТНОЙ нумерацией — тест с ПУСТОЙ главой между двумя непустыми: пустая глава номер не потребляет, а title_raw поедет параллельным срезом, и промах на одну позицию даст заголовок главы N на главе N+1 молча. Тот же класс уже чинили для ruby (ingest.go:568-605, denseNo).
  6. Именованный артефакт: посади дефект в разбор 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 несколько глав с одним номером. Это выбор рендера (номер книги против плотного ординала), он вне пака. Увидел — запиши числом в отчёт и иди дальше.
  • 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 вне books/.
  • Тесты и гейты не подгонять под зелень. ⚠ Правка, вызванная заказанной сменой поведения, — обслуживание. Заказанные здесь правки тестов, поимённо: · 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ГЛАВА BStructureDeclared), который становится delimited. И само имя 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. ЧТО ПРИСЛАТЬ И В КАКОМ ПОРЯДКЕ

  1. Эхо старта: своими словами — что понял предметом и что считаешь границей. До кода.
  2. Записка-план в отчёт: что делаешь, в каком порядке, и чем сдача осмысленна, если тебя оборвут на середине. До кода.
  3. Замеры шага 0 — числами, до правок.
  4. Код.
  5. Отчёт: батарея (команда, скипы числом, EXIT) · каталог · посадка именованного артефакта (file:line · что подменено · какое ИМЯ теста покраснело) · объявления по D39.183 · счета form feeds ignored / documents attached · манифест-фикстура с delimited · что НЕ проверено и почему.

Споткнулся о невыполнимое, о противоречие в самом задании или о нехватку доступа — пиши сразу, не обходи молча. Обход тихо становится нормой, и следующая смена наследует его как факт.