From 595a8ab46b64d14de805687f9af8507049cf52cd Mon Sep 17 00:00:00 2001 From: heaven Date: Fri, 18 Sep 2026 19:38:01 +0300 Subject: [PATCH] Add the chapter-boundary pack built on the measured seam, and retarget the drifted anchors across the notes, the experiments and the research files --- ...ACKEND_CHAPTER_STRUCTURE_SESSION_PROMPT.md | 402 ++++++++++++++++++ docs/BACKLOG.md | 8 +- docs/DOC_CLEANUP_PLAN.md | 2 +- docs/NEAR_TERM_PLAN.md | 6 +- docs/PROGRESS.md | 18 +- docs/architecture/03-implementation-notes.md | 4 +- docs/architecture/05-decisions-log.md | 42 +- docs/architecture/13-tech-debt-anchors.md | 4 +- docs/architecture/14-api-contract/README.md | 12 +- docs/architecture/15-money-path.md | 2 +- docs/architecture/17-seam-inbound-law.md | 4 +- docs/experiments/00-provider-quirks.md | 4 +- docs/experiments/23-editor-tier.md | 8 +- docs/orchestrator-norms-scratchpad.md | 4 +- .../31-harness-independent-critique.md | 2 +- docs/research/35-bank-memory-consilium.md | 6 +- 16 files changed, 465 insertions(+), 63 deletions(-) create mode 100644 docs/BACKEND_CHAPTER_STRUCTURE_SESSION_PROMPT.md diff --git a/docs/BACKEND_CHAPTER_STRUCTURE_SESSION_PROMPT.md b/docs/BACKEND_CHAPTER_STRUCTURE_SESSION_PROMPT.md new file mode 100644 index 00000000..f1ffe5c2 --- /dev/null +++ b/docs/BACKEND_CHAPTER_STRUCTURE_SESSION_PROMPT.md @@ -0,0 +1,402 @@ +# Промт бэкенд-сессии: ГРАНИЦА ГЛАВЫ — ФАКТ КНИГИ, А НЕ ДОГАДКА ДВИЖКА + +> Выдан оркестратором очереди №23. Пак **$0**. Зона записи — `backend/`. +> Ты не коммитишь: дерево передаётся оркестратору на лендинг. +> +> ⛔ **Это НЕ пак «починить осколки английской книги».** Осколки — симптом, и самый дешёвый из трёх. +> Предмет пака — **кто и на каком основании решает, где начинается глава**. + +--- + +## 1. Какая проблема и что решит твой результат + +Движок переводит книги мультиагентным пайплайном. Пары языков — ДАННЫЕ, не код; ревью-вопрос по умолчанию: +«заработает ли пара, которой в репо ещё НЕТ, без правки Go?». + +Скоро пойдёт платный холодный прогон двух книг: около 20 глав китайской и равный по токенам срез английской. +⛔ **Слово владельца, под которым выдан этот пак:** «цель — не бежать поскорее приступить к холодному +прогону, а подготовить бэкенд так, чтоб этот холодный прогон исполнить **максимально правильно**. Сколько +паков и бэклога нам для этого нужно закрыть, не очень важно. Халтурить не надо, подгонять тоже ничего не +надо, спиливаем долг из бэкенда, реализуем актуальный, нужный, чистый архитектурно код». + +**Что сломано, замерено мною по книге прогона (пере-снимешь своим прибором — все числа воспроизводимы $0).** +Целей навигации **71**: шесть ярлыков частей (`BOOK ONE`…`BOOK 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`, 14–82 тыс. знаков). + +⛔ **А настоящих глав в книге — 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`. +- ⇒ на заголовке С СЕПАРАТОРОМ после юнита (`第四章:…`) длиной 61–84 руны ингест говорит `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` напечатан счёт скипов. +- [ ] Явное **«работа завершена, править не планирую»**. + +⛔ **И последнее.** Предмет пака — **кто решает, где начинается глава**. Ответ, к которому надо прийти: +тождество решает движок, потому что это факт, а не догадка; АДРЕС границы решает грамматика книги; ИМЯ берётся +у того, кто его несёт. Всё, что сверх этого, решают данные книги. Если твоё +решение где-то ставит догадку движка на место данных — оно не готово, сколько бы тестов ни было зелёными. diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 112b2063..07c8f74a 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -176,7 +176,7 @@ | 108 | **Реестр ЗАГЕЙЧЕННЫХ ТРИГГЕРОВ (триггер → что вернётся → источник) — ВЫСЕЛЕН ДОСЛОВНО в `architecture/13-tech-debt-anchors.md` §Б-108; сверять на каждом переходе горизонта (норма D39.66 «каждое отложено получает носитель» в силе).** ⚠ Живое оттуда: go.work-триггер УЖЕ наступил (`platform/go.mod` заведён) — ⚠ **тот же факт несёт строка 237(а), испр. 05.09: чинится ОДИН раз и там, здесь он ЗАПИСЬ РЕЕСТРА, а не заказ** · вахта маппинга эффорта dspro «early August 2026» · движение тулчейна к go1.27 ⇒ строка 119 ПЕРВОЙ | оркестратор | скоро ($0, гигиена трекера) | завести реестр + сверку в норму лендинга | D39.66, D39.90 | | 109 | **Продуктовые требования без строки-носителя: ПТ-6 (структура абзацев) · ПТ-7 (слабый оригинал — вопрос владельца) · ПТ-22 (продуктовый интейк настроек) · ПТ-10 (механизм веб-фетча).** Носители заводить при первом касании тем; реестр `product-requirements.md` первичен. Разбор дословно — `architecture/13-tech-debt-anchors.md` §Б-109 | оркестратор/владелец | скоро ($0 — расписать носители) | завести строки/вопросы → диспозиции | D39.90, product-requirements | | 110 | **Детектор повторов лемм / защита авторских рефренов** (`04-unhappy-paths.md:97,130 п.5`): $0-минимум исполнен промптом (editor.md, пакет-чекеров 02.08), механизм явно не строился — «нет размеченного класса; связка с alignment/Bertalign». Строки-носителя не было; предмет живой, потому что отличить дефектный повтор от авторского рефрена без выравнивания нельзя | бэкенд/полигон | когда-нибудь (с корпусом пилота, строка 85) | размеченный класс → решение | 04-unhappy §5, D39.90 | -| 111 | **Дубль ключа пар-файла: фикс накрыл половину.** D39.82 закрыл строку 53 через `putUniqueDC` в `parseDCCheckers`, но генерик-парсер категорий `parseCategoryRows` (`internal/lang/langpack.go:806-826`=`for _, ln := range contentLines(b)`, строка `cats[f[0]][f[1]] = val`) по-прежнему МОЛЧА last-wins — а якорь техдолга указывал именно на него. Пар-данные с опечаткой-дублем тихо теряют первую строку | бэкенд | когда-нибудь (малое касание, с любой правкой langpack) | fail-loud + тест | 13-tech-debt-anchors, D39.82, D39.90 | +| 111 | **Дубль ключа пар-файла: фикс накрыл половину.** D39.82 закрыл строку 53 через `putUniqueDC` в `parseDCCheckers`, но генерик-парсер категорий `parseCategoryRows` (`backend/internal/lang/langpack.go:979`=`cats[f[0]][f[1]] = val` в `parseCategoryRows` (`backend/internal/lang/langpack.go:961`=`func parseCategoryRows`)) по-прежнему МОЛЧА last-wins — а якорь техдолга указывал именно на него. Пар-данные с опечаткой-дублем тихо теряют первую строку | бэкенд | когда-нибудь (малое касание, с любой правкой langpack) | fail-loud + тест | 13-tech-debt-anchors, D39.82, D39.90 | | 118 | **tools/go.mod — РЕШЕНИЕ ПРИНЯТО (D39.97 п.4): строить отдельным малым паком.** `cmd/tmvet`+`internal/archguard` → отдельный модуль (`backend/tools/`); `make battery` гоняет оба модуля; `tool`-директива Go 1.24 НЕ решает (MVS общий). Выигрыш: бамп инструментов физически не двигает x/net/x/text. Sqlite 1.55 остаётся (D39.97 п.4) | бэкенд | скоро (до следующего бампа зависимостей) | малый пак по D39.97 п.4 | D39.96 п.6, D39.97 | | 119 | **x/text-детерминизм: вердикт-несущие вызовы НЕ свёрнуты в версии (находки-5/6 приёмки).** `SanitizerVersion` не сворачивает ничего x/text-ного, а `detectCJKLeak` зависит от `width.Fold` (`sanitizer.go:211,687`) — смена x/text перевернула бы вердикт БЕЗ смены снапшота; `ExportNormalize` меняет экспортные байты вне хешей (`sanitizer.go:709`); `xTextVersion` пинит строку go.mod, НЕ скомпилированные таблицы — тулчейн go1.27 переключит x/text на Unicode 17.0.0 МОЛЧА (`tables17.0.0.go //go:build go1.27`; жилец реестра 108). Делать: явные версии-свёртки + механическую сверку «таблицы↔константа» вместо совпадения ⚠ **дописка 07.09 — stdlib-половина того же класса:** `unicode.Version` вплетён в 5 версионных констант, 4 из них — в НЕрепинуемые поля снапшота (`snapshot.go:428/429/431/447`), пятая через `NormVersion` уезжает в `cutTag` (`manifest.go:264`) ⇒ апгрейд тулчейна = громкий `--resnapshot` всех незакрытых книг. ⚠ Это НАМЕРЕННО (`render.go:53` — «weaving unicode.Version makes it a loud --resnapshot»), и вопрос строки не «дыра», а нужна ли громкость на осях estimator/cheapgate, которые вердикта не двигают (research/33 §7.1) | бэкенд | скоро | правка версий-свёрток + тест | D39.96 п.6, отчёт §5 находки-5/6 | | 120 | ⚠ **ЛИД УКАЗЫВАЕТ НА НЕСУЩЕСТВУЮЩУЮ ФУНКЦИЮ — испр. 05.09:** продового `buildCheckers` нет (`grep -rn 'buildCheckers' backend --include=*.go` → только тест-хелпер), и `lang.Load(` вне тестов не вызывается вовсе. Класс «бейзлайны ключуются по классу, не по паре» может быть жив — но искать его надо не здесь. ⚠ **Где искать (пере-снято 05.09): единственный прод-носитель бейзлайнов сегодня — `backend/internal/config/models.go`, и ключуются они по KIND ПРОВАЙДЕРА, а не по паре;** пара-специфика чекеров живёт данными `internal/lang`. ⇒ строка держится как ревью-вопрос общности («заработает ли пара, которой в репо НЕТ»), а не как известный адрес правки: **прежде чем строить — предъявить дефект на паре, которой нет.** **`buildCheckers` жёстко берёт `lang.Load(root,"zh","ru")`, бейзлайны ключуются по классу, не по паре (§7-8) — приезд ja-ru требует правки Go, против мандата общности §0.1.** Родня строки 115 (явный embed-список целей) — брать одним касанием | бэкенд | скоро | правка Go + тест на паре-которой-нет | D39.96 п.6, отчёт §7-8 | @@ -227,7 +227,7 @@ | 294 | ⭐ **ФОРМА «СКОЛЬКО ДОБАВИТЬ» — решение владельца 05.09, ратифицировано `D39.203`.** Наружу вместо «сколько не хватило на ЭТОТ вызов» едет **«сколько добавить, чтобы прошёл ЛЮБОЙ следующий»** = `max(shortfall, step_max − headroom)`: сегодняшняя недостача закрывает текущий вызов, а следующий может быть крупнее, и человек доплачивает дважды. ⚠ **НЕ однострочник, и объявлять его дешёвым было бы ошибкой** (зона дешевизну не подтвердила): новая величина · **ПЕРЕИМЕНОВАНИЕ поля** — `shortfall_micro_usd` станет ложным именем, кандидат `top_up_micro_usd` · минор `StreamVersion` 1.3→1.4 · зеркало словаря у платформы · пере-снятие пина белого списка кадра `ceiling`. ⚠ Построенное поле НЕ ложно, оно у́же: закрывает вызов, а не следующий шаг | бэкенд+платформа | скоро | новая величина + переименование + минор потока | **D39.203**; тензия п.(3) против п.(4) заказа найдена зоной движка | | 295 | ⛔ **СНАЧАЛА ПОДАТЬ ПОДПИСАННЫЙ КАНОН В ПРОГОН — только потом мерить точность чекера и решать про гейт.** Живая дыра приоритета №1 владельца («100% консистентность») — НЕ порог и НЕ флаг: канон из 53 подписанных терминов **не подключён к стенду**, весь замеренный дрейф — дрейф С ОТКЛЮЧЁННЫМ каноном, а там, где его подключали, консистентность 99.3–100%. ⇒ мерить полноту чекера на прогоне без канона значит мерить не то. **Порядок:** (1) канон в прогон · (2) пере-замер точности/полноты (строка **12**, прежний замер порочен — класс одного иероглифа не имел ни одного положительного примера) · (3) решение владельца по `gates.glossary.postcheck_gate`. ⚠ Владелец 05.09: «если он ложно положительно будет стрелять это плохо, очень плохо» ⚠ **ПОЛОВИНЫ НАЗВАНЫ 06.09 по ревью напарника — прежде тут стояло «назвать или отдать», и это переезжало бы следующей смене той же инерцией.** **(а) Полигон/владелец:** подать канон в стенд-книгу — это ДАННЫЕ (сид-YAML либо решения `bank-apply`) ПЛЮС платный прогон под санкцией; у бэкенд-сессии нет ни книги, ни стенда, ни денег, поэтому половина её не является. **(б) Бэкенд — только ВОПРОСОМ, а не задачей:** умеет ли сид-путь нести подписанный статус; если нет — это пинг оркестратору, и тогда появляется бэкендовая работа. ⇒ зона ведущая — полигон. ⛔ **ИСПР. 11.09 АУДИТОМ БАНКА — ГОЛОВНАЯ ПОСЫЛКА РЯДА ВЕРНА ТОЛЬКО ПРО СТЕНД.** Пере-снято: «канон ни разу не подан в прогон» справедливо для ИССЛЕДОВАТЕЛЬСКОГО стенда (там 0 конфигов из 30 несут ключ сида), но НЕ для дерева: ключ `glossary_seed` задан в **18** конфигах (прибор — `yaml.safe_load` по 150 `.yaml` в `books/gu-zhenren`, контроль: не распарсилось 0; греп подстроки давал 25, и в шести из них ключа НЕТ). Пять прогонов держат 36–57 подписанных строк, а весь размеченный корпус (91 единица) снят с прогонов С каноном, и дрейф ПРИ каноне наблюдён живьём (`古月方源` → «Гу Юэ Фан Юань» при подписанном `古月 → Гуюэ`). ⇒ порядок работ меняется: это не «сперва подключить канон», а **«разметить класс, которого в корпусе не было»** — $0 на уже оплаченных артефактах плюс новый прогон. Носитель разбора — `docs/research/34-memory-bank-quality-audit.md`; прямая причина низкой точности названа рядом **408** (данные, не код). | полигон (бэкенд — по вопросу) | скоро | подача канона + замер | **D39.205**, D39.198 п.3-4, строка 12 | | 296 | **ТОЧЕЧНАЯ ПЕРЕГЕНЕРАЦИЯ ПО ПРОМАХУ ГЛОССАРИЯ — направление владельца 05.09 («или флагать, или перегенерировать проблемный участок»), работы сегодня НЕТ.** Актуаторы ремонта — только `RepairLatinResidue` и `RepairBrokenWord` (`backend/internal/pipeline/repair.go`, греп `defaultRepairClasses`); `glossary_miss` нет ни там, ни в `knownRepairClasses`. Сегодняшние лекарства не точечные и обе дороги: удержать ВЕСЬ фрагмент (гейт) либо править глоссарий с `--resnapshot`, то есть ПЕРЕ-ОПЛАТИТЬ книгу. ⇒ заказ: `glossary_miss` как класс-актуатор — перегенерация участка с подписанной формой. ⚠ Гейтится строкой **295**: без замера точности перегенерация по ложному срабатыванию перепишет верный текст | бэкенд | когда-нибудь (после 295) | новый класс актуатора + пост-инвариант | **D39.205** | -| 297 | ✅ **ПЕРВАЯ ПОЛОВИНА ИСПОЛНЕНА ЛЕНДИНГОМ 06.09 (`2f65d1d`, акт `D39.210`): словарь получил четвёртое значение `delimited`**, и spine-EPUB вместе с `\f` в txt больше не выдаются за слово формата. ⛔ **ВТОРАЯ ПОЛОВИНА ТЕПЕРЬ ОБЯЗАТЕЛЬНА, А НЕ ЖЕЛАТЕЛЬНА:** временное правило платформы «`declared` вести как `none`» после лендинга начинает ОТНИМАТЬ ползунок глав у книг, чья структура объявлена настоящим `nav`/NCX — то есть молча недодавать продукт. ⚠ **Якоря прежней редакции этой строки указывали на текст, который пак УДАЛИЛ, а якорный линт этого НЕ ВИДЕЛ** — они были без токена, файл длинный, адрес «разрешался». Живая рабочая строка протухла молча; класс несёт строка **306**, и это его первый пойманный случай в рабочей строке, а не в архиве. ⚠ **Тело прежней редакции СНЯТО 06.09, а не оставлено под шапкой** — оно цитировало рекламу «how much the chapter cut can be trusted» как живую улику, а пак её УДАЛИЛ (`grep -c` → 0), и три его якоря целили в текст, которого нет: словарь уехал, а `:94-95` попал в структуру, введённую дофиксом. То есть строка утверждала про себя, что она первый пойманный случай «якорь протух молча», и одновременно продолжала им БЫТЬ; поймал это строитель пака грепом, линт был зелён. **Что стало вместо снятого:** словарь живёт на `backend/internal/chunk/ingest.go:125-149`=`WHICH PATH drew the chapter boundaries` — четыре значения, и первая же строка комментария теперь говорит про ПУТЬ, а не про доверие. Прежняя двусмысленность («поле отвечает на провенанс, а рекламирует доверие») **устранена паком, а не описана** | бэкенд+платформа | скоро (структурный пак) | недостающее значение на проводе, и `declared` означает ТОЛЬКО объявленную форматом структуру глав | **D39.202**, D39.205 | +| 297 | ✅ **ПЕРВАЯ ПОЛОВИНА ИСПОЛНЕНА ЛЕНДИНГОМ 06.09 (`2f65d1d`, акт `D39.210`): словарь получил четвёртое значение `delimited`**, и spine-EPUB вместе с `\f` в txt больше не выдаются за слово формата. ⛔ **ВТОРАЯ ПОЛОВИНА ТЕПЕРЬ ОБЯЗАТЕЛЬНА, А НЕ ЖЕЛАТЕЛЬНА:** временное правило платформы «`declared` вести как `none`» после лендинга начинает ОТНИМАТЬ ползунок глав у книг, чья структура объявлена настоящим `nav`/NCX — то есть молча недодавать продукт. ⚠ **Якоря прежней редакции этой строки указывали на текст, который пак УДАЛИЛ, а якорный линт этого НЕ ВИДЕЛ** — они были без токена, файл длинный, адрес «разрешался». Живая рабочая строка протухла молча; класс несёт строка **306**, и это его первый пойманный случай в рабочей строке, а не в архиве. ⚠ **Тело прежней редакции СНЯТО 06.09, а не оставлено под шапкой** — оно цитировало рекламу «how much the chapter cut can be trusted» как живую улику, а пак её УДАЛИЛ (`grep -c` → 0), и три его якоря целили в текст, которого нет: словарь уехал, а `:94-95` попал в структуру, введённую дофиксом. То есть строка утверждала про себя, что она первый пойманный случай «якорь протух молча», и одновременно продолжала им БЫТЬ; поймал это строитель пака грепом, линт был зелён. **Что стало вместо снятого:** словарь живёт на `backend/internal/chunk/ingest.go:158`=`WHICH PATH drew the chapter boundaries` — четыре значения, и первая же строка комментария теперь говорит про ПУТЬ, а не про доверие. Прежняя двусмысленность («поле отвечает на провенанс, а рекламирует доверие») **устранена паком, а не описана** | бэкенд+платформа | скоро (структурный пак) | недостающее значение на проводе, и `declared` означает ТОЛЬКО объявленную форматом структуру глав | **D39.202**, D39.205 | | 298 | ⛔ **`unit_resolutions` КЛЮЧЕВАНЫ ПОЗИЦИЕЙ, И ЛЮБОЙ ПЕРЕКРОЙ ИХ СНОСИТ ЦЕЛИКОМ.** Ключ `(book_id, chapter, unit, wave)` позиционен по построению, и миграция это прямо объявляет: «keyed by the ENGINE's own ordinals» (`platform/internal/pgstore/migrations/00015_seam_ceiling_and_units.sql:48-56`=`keyed by the ENGINE's own ordinals` — ⚠ испр. 16.09: было :50-56); событие `unit_done` несёт номер главы и ординал юнита, синк пишет их как есть (`platform/internal/pgstore/sink.go:249`). Потому при перекрое платформа ОБЯЗАНА удалять решения — иначе они перевесятся на чужую главу. Сегодня цена нулевая: решения есть только у стенд-книги, и это окно закрывается с каждой новой платной книгой. ⇒ разрешать `(chapter, unit)` в идентификатор ПРИ ПРИЁМЕ события, пока дерево у платформы на руках, и хранить идентификатор. Платформенная половина структурного пака: миграция плюс синк. ⚠ Этой строкой НЕ закрывается одноразовая перечеканка всех id при смене смысла `cutTag` — это другой предмет и другая цена | бэкенд+платформа | скоро (структурный пак) | решения переживают перекрой, не переклеиваясь на чужую главу | **D39.202** | | 299 | ⛔ **ИМЯ В GO-КОММЕНТАРИИ, КОТОРОЕ НИ ВО ЧТО НЕ РАЗРЕШАЕТСЯ, — И НИЧТО ЭТОГО НЕ ЛОВИТ.** ⭐ **ЗАМЕР 05.09, полностью меняющий форму ДОРОГОЙ половины (той, что сверяет имя с каталогом мутаций): она обязана писать ФАЙЛ, а не читать `stdout`.** Полный прогон каталога дал: несущих верхнеуровневых тестов **189** из 1213 тест-функций движка (**15,5 %** батареи; ⚠ испр. 06.09 автором замера: было «194 / 16,0 %» — его парсер резал вывод по запятой, а пять подтестов несут запятую В ИМЕНИ, и круглые «16,0 %» получились именно из пяти фантомов. ⚠ Числитель и знаменатель разной природы: 1213 считает только `^func Test`, а один из 189 — `Fuzz`; честно 188/1213 либо 189/1219, но не 189/1213), и **139 записей из 197 пинятся РОВНО ОДНИМ тестом** — то есть краснота атрибутируется однозначно, и долговременная запись «какие тесты несущие» реально получается. ⛔ **Но тул обрезает список имён на четырёх и печатает `+N more` — так усечены 9 записей из 197, и механизм, читающий `stdout`, насчитал бы 178 вместо 194 И НЕ УЗНАЛ БЫ ОБ ЭТОМ.** Это тот же класс «прибор ответил на свой вопрос»: величина ВЫЧИСЛЯЕТСЯ сегодня, но в поток приезжает УСЕЧЁННОЙ. ⇒ вход механизма — `-logs`, не вывод. Точный список имён воспроизводится пере-прогоном каталога с `-logs`; сохранять его отдельным артефактом не нужно. Два живых носителя, найдены верификацией 05.09 (`D39.207` п.3): **(а)** `backend/internal/runevents/runevents.go:187`=`not to a screen` цитирует **СТАРЫЙ** путь — `docs/BACKEND_MONEYSTOP_SESSION_PROMPT.md`, строка 78 (записано БЕЗ формы якоря намеренно: это УЛИКА мёртвой ссылки, а не ссылка; в форме якоря линт вечно краснел бы на предмете самой записи) — и удалён именно он (промт уведён в архив коммитом `f71c4dc`, тем же, что писал акт), а цитируемая фраза ЖИВА в архивной копии и стоит там на строке **82**. ⚠ **Испр. 06.09: клауза была заландена ПЕРЕВЁРНУТОЙ** — называла удалённым архивный файл, который цел, и приписывала комментарию цитату, которой в нём нет. Механика ошибки записана отдельно, она повторяемая: сплошная замена строки якоря при пере-наведении попала в ОПИСАНИЕ дефекта, где тот же путь был УЛИКОЙ, а не ссылкой, и превратила описание поломки в описание её отсутствия. Нашёл верификатор `textmachine-main-e6` контролем закрытия своих же находок; ⚠ приёмочный столбец строки при этом сформулирован ВЕРНО и поймал бы дефект независимо от прозы — то есть major в описании, не в гейте; **(б)** `backend/internal/config/internal_call.go:11`=`builds a Stage outside the loader` и `backend/internal/pipeline/live_reprobe_test.go:14`=`the single-egress guard` называют сторожами тесты `TestSyntheticStageSeamIsSingle` и `TestProviderEgressSeamIsSingle`, которых в дереве НЕТ — инварианты при этом реально сторожатся вет-анализаторами `StageSeam`/`EgressSeam` (`backend/internal/archguard/archguard.go:80,377`=`StageSeam, RequestSeam, EgressSeam`), то есть гарантия цела, а назначенный ей сторож ВЫДУМАН. Замер верификатора: из 2026 упомянутых в Go имён `Test…` определены 2012; из восьми неразрешающихся четыре — не дефекты (надгробия и иллюстрация), два — мелочь, два — эти. ⇒ **дешёвая половина открытого вопроса `D39.202` п.9: не «доказал ли каталог, что названный тест умеет падать» (дорого, требует интеграции с `tmmutate`), а «РАЗРЕШАЕТСЯ ли названное имя вообще» — это греп, и сегодня он находит два дефекта.** ⚠ `counts.py --lint` этого класса не видит по построению: он читает доки, а не код. ⚠⚠ **ДВА ПРАВИЛА, БЕЗ КОТОРЫХ ГЕЙТ ВЫКЛЮЧАТ НА ВТОРОЙ ДЕНЬ** (принесены верификатором вместе с находкой): **(1)** разрешением считается и ПРЕФИКС — доки и комментарии цитируют имя усечённым, как греп-цель; **(2)** НАДГРОБИЯ не дефект — комментарий, прямым текстом говорящий «удалён/заменён», законен, как и иллюстративное имя в примере. Замер, обосновывающий оба: из восьми неразрешающихся имён СЕГОДНЯ четыре — надгробия и иллюстрация, два — усечение в шапке над функцией, и лишь два — настоящие выдуманные сторожа. Гейт без этих правил краснеет 8 из 8, то есть врёт вчетверо, и его погасят — ⚠⚠ **и ЗАМЕР 06.09 показал, что для указателей `путь:строка` в Go арифметика ХУЖЕ, а потолок дешёвой половины НИЖЕ, чем считалось.** Всего таких указателей **36**; настоящий дефект среди них **ОДИН** (`runevents.go:187`, уже назван выше). Остальное: **5 целят ВНЕ репозитория** (стандартная библиотека Go, `yaml.v3`, jieba) — внутрирепозиторный линт их проверить не может в принципе; **~7 разрешаются НЕОДНОЗНАЧНО** — голое имя файла имеет от двух до восьми одноимённых кандидатов. ⇒ наивный гейт покраснел бы примерно **двенадцать раз ложно при одном настоящем дефекте**. ⛔ **ДВА правила записать ДО заказа, иначе гейт врёт впятеро и его выключат:** **(1)** внешняя цель — НЕ дефект (путь, не разрешающийся в дереве и опознаваемый как чужой, исключается); **(2)** ⛔ **у голого имени файла SOUND-ДЕФОЛТА НЕТ — гейт обязан считать его НЕПРОВЕРЯЕМЫМ (skip), а не угадывать.** ⚠ Прежняя редакция этого пункта предлагала конвенцию «сперва каталог цитирующего файла, затем модуль, затем корень» — **и она ОПРОВЕРГНУТА 06.09 её же автором**: для дока, цитирующего КОД, каталог цитирующего файла систематически неверен, потому что рядом с доком лежат другие ДОКИ. Контрпример — `PROGRESS.md:71` → `README.md:95`: конвенция даёт `docs/README.md` (41 строка) и вердикт «протух», а автор имел в виду `backend/README.md`, где строка 95 **та же, что в минуту написания**. В дереве `README.md` четырнадцать штук. ⇒ гейт с конвенцией краснел бы именно на ЖИВЫХ якорях. Хочешь проверяемости — пиши путь или токен. ⚠ И третье, важнее обоих: **НИ ОДИН из 36 не несёт токена**, поэтому ДОРОГАЯ половина упирается не в инструмент, а в то, что сверять нечего; дешёвая остаётся верным первым шагом, но её потолок теперь измерен — **один дефект на тридцать шесть**. а гейт умирает не отключением, а вытеснением из привычки (`D39.202` п.3-бис) | бэкенд | скоро | грепом: имя `Test…` и `<док>.md:<строка>` в Go разрешаются, иначе красно | **D39.207**, D39.202 | | 300 | ⛔ **РЕПО-ШИРОКИЙ СТОРОЖ НЕ ДОТЯГИВАЕТСЯ ДО СОСЕДНЕГО ВОРКРИ НА ДРУГОЙ ВЕТКЕ — и стенд от этого УЖЕ СГНИЛ.** ⚠ Замер 05.09, вторая половина: снять пин манифеста стенд-конфигами удалось для **ОДНОГО исходника из восьми**; семь падают, и ни одно падение не про структурный пак. Причины — ретированные в `main` вещи, живые в дереве `polygon`: плейсхолдер `{{genre}}` (ретирован `D39.138` п.2в) — в моём дереве `grep -rln '{{genre}}' backend/prompts/` ПУСТ, в соседнем живёт в ШЕСТИ файлах промтов, и падают на нём 6 конфигов из 8; поля `stm_depth`/`overlap_tokens`, которых больше нет в `config.ContextAssembly` — 1 из 8; ключи `mined_delta:`/`mined_rejects:` (ретированы `D39.156` п.3) — **17 конфигов из 31**. ⭐ **Тест `genre_retired_test.go:46` предсказал этот случай дословно** («should be caught by the repository that retired it, not by a paid run») — и поймали его действительно бесплатно, но **сторож не дотянулся до соседней ветки**, где ретированное живо. ⇒ общая форма дефекта: ретирование, проведённое репо-широким гейтом, НЕ распространяется на воркри другой ветки, и стенд тихо остаётся в прошлом. **ЧАСТНЫЙ СЛУЧАЙ ТОГО ЖЕ КОРНЯ — ДАННЫЕ ПАР-ПАКА В ДВУХ ВЕТКАХ.** Замер бэкенд-сессии 05.09 по 31 конфигу `book.yaml` стенда 蛊真人: у **20** `langpack_root` указывает в СОСЕДНЕЕ РАБОЧЕЕ ДЕРЕВО `/home/ubuntu/projects/textmachine` — а оно на ветке **`polygon`**; у 9 — в `main`; 2 не объявляют его вовсе. Пока каждая ветка собирается из своего дерева, расхождения не видно. ⚠ **Структурный пак (`CHAPTER_STRUCTURE_SESSION_PROMPT.md`) выносит `marker`/`units` из `zh-ru/heading.txt` в данные ИСТОЧНИКА и учит `parseHeading` ОТВЕРГАТЬ эти ключи с переходным сообщением** ⇒ старый файл, оставшийся на `polygon`, станет невалидным, и двадцать конфигов не загрузятся вовсе (загрузка пар-пака FAIL-LOUD). **Выстрелит НЕ при лендинге в `main`, а в момент, когда полигон вольёт `main` в свою ветку** — то есть цена назначена чужой зоне и в чужой момент. ⇒ заказ: перед мержем полигон либо снимает `marker`/`units` из своего `heading.txt`, либо переводит `langpack_root` своих конфигов на `main`. ⚠ Второе дешевле и, возможно, правильнее по существу: **каталоги `configs/langpacks` в обоих деревьях сегодня БАЙТ-В-БАЙТ идентичны** (`diff -rq` пуст, sha256 `heading.txt` совпадают), а комментарии в конфигах, утверждающие «`ru/` есть только здесь», ПРОТУХЛИ — класс строки 299. То есть двадцать конфигов смотрят в чужую ветку по причине, которой больше нет | полигон | скоро (до мержа main в polygon) | конфиги грузятся после мержа, либо `langpack_root` переведён | **D39.207** | @@ -274,7 +274,7 @@ | 343 | **`counts.py` ДЕРЖИТ РЕГИСТР ОДНИМ ПУТ�ём И НЕ ГЛОБИТ СЛАЙСЫ — предусловие нарезки `DEFECT_REGISTER.md`.** Вынос без этого молча уронит счёт открытых рядов, а гейт чисел покажет зелень: он читает ровно один файл. ⚠ План нарезки живёт в `docs/DOC_CLEANUP_PLAN.md` (Б14в) | доки | скоро | прибор читает регистр и его слайсы, счёт не меняется при выносе | вопросы платформы 07.09 | | **— ТЕХДОЛГ БЭКЕНДА: разбор research/33 (D39.224) —** | | | | | | | 346 | ⚠ **ПОЛОВИНА (а) ЗАКРЫТА 07.09 (`D39.225`): пин расхождения и паритет-тест стоят, оба под мутациями. ОСТАЁТСЯ (б) — фикс в окне перенарезки.** ⛔ **ПРАВИЛО ЗАГОЛОВКА РАЗЪЕХАЛОСЬ ПО ТРЁМ ПРЕДИКАТАМ, И «第零章» ЛОМАЕТ ПРОЛОГ — две половины с РАЗНОЙ ценой.** Страж длины живёт только в ингесте (`backend/internal/chunk/ingest.go:341`=`const chapterHeaderMaxRunes = 60` `chapterHeaderMaxRunes = 60`, применён `backend/internal/chunk/ingest.go:357`=`> chapterHeaderMaxRunes`; в `chunker.go` `RuneCountInString` — 0 хитов), осмысленность номера — только в чанкере (`backend/internal/chunk/chunker.go:315-317`=`v := section + num` — ⚠ испр. 16.09: было :316, где утверждение несёт строка с `||`, непригодной для ячейки таблицы `if !any || v <= 0`). Воспроизведено исполнением 07.09 (выброшенная проба на копии дерева): `第零章:序幕` ингест режет главой, а чанкер заголовок НЕ снимает — `heading=""`, сырой CJK-маркер уезжает модели; `parseSectionNumeral` на `零`/`0`/`〇` даёт `0,false` (контроль: `一`=1, `十二`=12); номера расходятся (`第一章` = `Chapter 2` при «Глава 1»); 84-рунный заголовок: ингест `false`, чанкер `ok=true`. ⚠ **Испр. 07.09 — без этой оговорки строка читается как ЛОЖНАЯ:** расхождение живёт ТОЛЬКО у заголовка С СЕПАРАТОРОМ после юнита (`第四章:…`); без сепаратора оба предиката дают `false` на любой длине, потому что такую строку отвергают и `isHeaderSeparator` ингеста, и `matchHeaderLine` чанкера. Пере-снято оркестратором на копии `d6dfc49`: с сепаратором 59 и 60 рун — оба `true`; 61 и 84 — ингест `false` при чанкере `ok=true n=4`; без сепаратора 12/55/59/60/61/84 — `false`/`false` во всех шести. Граница ровно `chapterHeaderMaxRunes` = 60. Перекрёстного теста нет ни в одном из 97 тестов `internal/chunk`. **(а) $0 СЕЙЧАС — пин ИЗВЕСТНОГО расхождения, а не паритет:** красный тест в `main` не ложится (`Makefile:58` `battery: build vet fmt lint test`), скип — класс D39.208; поэтому тест утверждает, что на `第零章` и на 61–84-рунной строке ингест и чанкер СЕГОДНЯ расходятся, назван этой строкой и краснеет ровно тогда, когда приземлится фикс (б), заставляя фиксера его перевернуть. Дизайн-пак 161 §4.5 паритет-тест ОПИСЫВАЕТ, пишет его пак стройки — пин (а) этому не мешает. **(б) В ОКНО ПЕРЕНАРЕЗКИ:** сам фикс меняет ТЕКСТ чанка ⇒ бампает `chunkerVersion` ⇒ снапшот обеих волн (`render.go:55-58` — «a deliberate re-translation»); отдельным актом он платит ту самую перепокупку, которую отменяет. ⚠ Ось «обязательность юнита» — НЕ трогать: носитель строка **303** (⚠ испр. 07.09: research/33 приписывал её `D39.207`, в теле ноты этого нет — греп по предмету 0 при 23 строках тела) | бэкенд | скоро для (а); (б) — в окне перекроя (шаг 3) | (а) пин расхождения · (б) сведение двух предикатов к ингестовским стражам внутри пака перекроя | research/33 Д3, research/27 §6 | -| 350 | ⚠ **ИСПОЛНЕНО 07.09 (`D39.225`) КРОМЕ ОДНОГО: по каждой из шести функций назван исход — четыре получили причину жизни комментарием («NO CALLER TODAY: contract surface waiting on backlog row 12»), дубль инлайна свёрнут в `finalStageName()`. ОСТАЁТСЯ словарь родов в четырёх носителях; текст его собственной ошибки врать перестал.** **РЕФАКТОРИНГ ОБЛЕГЧЕНИЯ ВЕДЕНИЯ (санкция владельца 07.09) — снапшот-нейтрально, и заказ здесь «решить», а не «удалить».** Вызовов вне определения ноль у шести функций (греп 07.09; КОНТРОЛЬ — `SplitChunks` 34 живых вызова): `obs.SafeGo`, `text.TokenizeCyrillic`, `CheapGateResult.UnitScaleHard`/`UnitScaleSoft`, `Bank.Voices()`, `Bank.Pairs()`. ⚠ **Четыре из шести стоят НАМЕРЕННО, и слепое удаление снимет намеренное:** `UnitScaleHard/Soft` сами называют, кого ждут — `checks/cheapgates.go:155-156`=`Total() is unchanged` «for the report and a future hard gate», то есть строку **12**; `TokenizeCyrillic` — образец паттерна value-default, на который ссылаются `backend/internal/checks/sanitizer.go:21`=`value-default pattern, like text.TokenizeCyrillic` и `:197`; `SafeGo` — намеренный контраст к `obs/panic.go`, названный решением `backend/internal/pipeline/waverun.go:420`=`NOT obs.SafeGo` — ⚠ испр. 16.09: было :405 («NOT obs.SafeGo»), и его удаление повесит два комментария в воздухе — класс строки **299**; `Voices`/`Pairs` (`membank/memory.go:371-376`=`address-register journal (frozen for` «frozen for the job») ждут потребителя, которого держит строка **13б**. ⇒ по каждой ОДИН исход с объявлением в отчёте: удалить ЛИБО назвать причину жизни в комментарии. Плюс ИМЯ последней стадии дважды одинаковым инлайном — `r.Pipeline.Stages[n-1].Name` — ⚠ **испр. 16.09: ДУБЛЬ УСТРАНЁН, предмет закрыт кодом:** оба места берут теперь общий хелпер (`backend/internal/pipeline/export.go:281`=`lastStage := r.finalStageName()` и `quality.go:485` тем же вызовом; инлайна `Stages[n-1].Name` в обоих файлах ноль, `finalStageName` по движку 4 вхождения). Прежний адрес был `pipeline/export.go:283` и `quality.go:342` (адреса на `d6dfc49`). ⚠ **Испр. 07.09: и research/33 §7.1, и первая редакция этой строки называли здесь «готовый `finalStageWave()`» — это ДРУГАЯ функция**: она возвращает ВОЛНУ (`snapshot.go:247`, вызовов 13 — 10 прод и 3 теста), а хелпера для ИМЕНИ не существовало вовсе, и «при готовом хелпере» читалось как «работа уже сделана» и словарь родов в четырёх носителях (`membank/memory.go`, `membank/memseed.go`, `backend/internal/terminology/classify.go:40`=`Genders is the closed vocabulary` — ⚠ испр. 16.09: было :50, промт), где текст собственной ошибки врёт о принимаемом множестве. ⛔ Границы санкции: НЕ дробить `internal/pipeline` и НЕ переписывать `Runner` (`12-go-style-notes.md` §1, D39.23), ничего, что двигает снапшот / хеш запроса / ключ манифеста. ⚠ Адреса сняты research/33 на `e69902a`, а лендинг 07.09 тронул `memseed.go` и `export.go` — пере-снимать командой, не по тексту | бэкенд | когда-нибудь | попутный проход с батареей + объявление исхода по каждой функции | research/33 §7.1, санкция владельца 07.09 | +| 350 | ⚠ **ИСПОЛНЕНО 07.09 (`D39.225`) КРОМЕ ОДНОГО: по каждой из шести функций назван исход — четыре получили причину жизни комментарием («NO CALLER TODAY: contract surface waiting on backlog row 12»), дубль инлайна свёрнут в `finalStageName()`. ОСТАЁТСЯ словарь родов в четырёх носителях; текст его собственной ошибки врать перестал.** **РЕФАКТОРИНГ ОБЛЕГЧЕНИЯ ВЕДЕНИЯ (санкция владельца 07.09) — снапшот-нейтрально, и заказ здесь «решить», а не «удалить».** Вызовов вне определения ноль у шести функций (греп 07.09; КОНТРОЛЬ — `SplitChunks` 34 живых вызова): `obs.SafeGo`, `text.TokenizeCyrillic`, `CheapGateResult.UnitScaleHard`/`UnitScaleSoft`, `Bank.Voices()`, `Bank.Pairs()`. ⚠ **Четыре из шести стоят НАМЕРЕННО, и слепое удаление снимет намеренное:** `UnitScaleHard/Soft` сами называют, кого ждут — `backend/internal/checks/cheapgates.go:155-156`=`Total() is unchanged` «for the report and a future hard gate», то есть строку **12**; `TokenizeCyrillic` — образец паттерна value-default, на который ссылаются `backend/internal/checks/sanitizer.go:21`=`value-default pattern, like text.TokenizeCyrillic` и `:197`; `SafeGo` — намеренный контраст к `obs/panic.go`, названный решением `backend/internal/pipeline/waverun.go:420`=`NOT obs.SafeGo` — ⚠ испр. 16.09: было :405 («NOT obs.SafeGo»), и его удаление повесит два комментария в воздухе — класс строки **299**; `Voices`/`Pairs` (`backend/internal/membank/memory.go:371-376`=`address-register journal (frozen for` «frozen for the job») ждут потребителя, которого держит строка **13б**. ⇒ по каждой ОДИН исход с объявлением в отчёте: удалить ЛИБО назвать причину жизни в комментарии. Плюс ИМЯ последней стадии дважды одинаковым инлайном — `r.Pipeline.Stages[n-1].Name` — ⚠ **испр. 16.09: ДУБЛЬ УСТРАНЁН, предмет закрыт кодом:** оба места берут теперь общий хелпер (`backend/internal/pipeline/export.go:281`=`lastStage := r.finalStageName()` и `quality.go:485` тем же вызовом; инлайна `Stages[n-1].Name` в обоих файлах ноль, `finalStageName` по движку 4 вхождения). Прежний адрес был `pipeline/export.go:283` и `quality.go:342` (адреса на `d6dfc49`). ⚠ **Испр. 07.09: и research/33 §7.1, и первая редакция этой строки называли здесь «готовый `finalStageWave()`» — это ДРУГАЯ функция**: она возвращает ВОЛНУ (`snapshot.go:247`, вызовов 13 — 10 прод и 3 теста), а хелпера для ИМЕНИ не существовало вовсе, и «при готовом хелпере» читалось как «работа уже сделана» и словарь родов в четырёх носителях (`membank/memory.go`, `membank/memseed.go`, `backend/internal/terminology/classify.go:40`=`Genders is the closed vocabulary` — ⚠ испр. 16.09: было :50, промт), где текст собственной ошибки врёт о принимаемом множестве. ⛔ Границы санкции: НЕ дробить `internal/pipeline` и НЕ переписывать `Runner` (`12-go-style-notes.md` §1, D39.23), ничего, что двигает снапшот / хеш запроса / ключ манифеста. ⚠ Адреса сняты research/33 на `e69902a`, а лендинг 07.09 тронул `memseed.go` и `export.go` — пере-снимать командой, не по тексту | бэкенд | когда-нибудь | попутный проход с батареей + объявление исхода по каждой функции | research/33 §7.1, санкция владельца 07.09 | | 351 | **ЛИНТ СЛОВАРЯ КЛАССИФИКАТОРА ПИНИТ ОДНО НАПРАВЛЕНИЕ: слово, УБРАННОЕ из Go, не ловит ничто.** Гейт `TestEveryClassifierPromptSpeaksTheEnginesVocabulary` (лендинг `D39.225`) утверждает «каждое слово из `terminology.Types`/`Genders` названо в промте пары» — и потому сжимается вместе со своим предметом: выкинутый класс `title` оставляет батарею зелёной (замер 07.09 на копии: 19 ok / 0 FAIL). Вырожденный предел закрыт стражем `len(classifierVocabulary()) == 0`, общий случай — нет. Настоящий дом — пин САМОГО словаря, которого в репозитории нет (research/33 Д1: 0 тестов на `terminology.Types`). ⚠ Граница названа зоной самостоятельно, в заказ пака не входила | бэкенд | когда-нибудь | пин словаря (голден или рефлексивный тест) | `D39.225`, research/33 Д1 | | 352 | **ПЯТЬ РУКОПИСНЫХ СПИСКОВ АРМ-КОНФИГОВ ЖИВУТ РЯДОМ С ДВУМЯ ГЕЙТАМИ, КОТОРЫЕ ТЕПЕРЬ ГЛОБЯТ.** `backend/internal/pipeline/promptlabel_test.go:45`=`pipeline-arm-glm.yaml` · `backend/internal/config/echoregen_shipping_test.go:35`=`pipeline-arm-glm.yaml` · `backend/internal/config/content_labels_test.go:529`=`pipeline-c1.yaml` и `:575` · `backend/internal/config/prompt_pack_test.go:240`=`pipeline-arm-mistral.yaml` перечисляют армы поимённо; новый арм автоматически попадает в `TestAnArmIsTheProductionConfigWithADifferentEditor` и `TestSwapArmConfigs` и проходит мимо всех пяти. Найдено приёмкой `D39.225`, вне заказа пака; предсуществующее | бэкенд | когда-нибудь (со следующим касанием армов) | свести пять списков к глобу | `D39.225` | | 353 | **КОНТРАКТ СЕКЦИИ ПРЕДЛОЖЕНИЙ В САЙДКАРЕ БАНКА ЗАПИНЕН ПО ПОЛЯМ И ПОРЯДКУ, НО НЕ ИСЧЕРПЫВАЮЩЕ — строка заведена ПРАВИЛОМ ОСТАНОВКИ, а не находкой.** Три круга приёмки `D39.225` находили в одной новой секции всё более узкие дыры: сперва семь полей без утверждений, потом `Freq` и своп списков, потом ранжирование и класс пустого `Dst`. Каждая была настоящей и предъявлена посадкой; каждый следующий круг стоил больше и покупал меньше. Сегодня запинены: все решающие поля против стоп-строки · порядок против стоп-СТРОК на двух уровнях · рефлективный страж «ни одно поле проекции не нулевое» (ловит поле, добавленное завтра и не заполненное). ⚠ **Дописка 08.09 (приёмка `D39.226`):** у поля `SettledByBank` стоп-строки нет свидетеля в списке «полей, которых сайдкар намеренно НЕ несёт» (`bankexport_test.go`) — то есть его сознательное отсутствие в проекции не утверждено ничем; закрывать вместе с первым читателем секции. ⚠ Границу называет сам гейт: поле, которое отрастил `BankStopRow` и которое СЛЕДОВАЛО бы проецировать, механически не ловится — это суждение того, кто поле добавляет. ⇒ вернуться к исчерпывающему контракту, когда у секции появится ЧИТАТЕЛЬ (платформенная половина строки **224**): пока читателя нет, расхождение ничем не судится, а в день его появления разница станет семантикой задним числом — второй ярус класса A `18-bank-ontology.md` | бэкенд + платформа | когда-нибудь (гейт: появление читателя секции, строка 224) | исчерпывающий пин контракта секции вместе с её первым потребителем | `D39.225` п.10 | @@ -317,7 +317,7 @@ | 403 | **ЦЕПОЧКА «СВИП → ОСТАНОВКА → НАСТОЯЩИЙ systemd» НЕ ПРОЙДЕНА НИЧЕМ, и это объявлено ЗАРАНЕЕ, а не найдено потом.** Платформенная сессия дважды за смену упиралась в одно: пины говорят о ЮНИТЕ (живой systemd-стенд у зоны есть) либо о СТОРЕ (живой Postgres есть), а утверждения о поведении живут в СВИПЕ — и стенда, который проводит служебный обход через настоящую остановку настоящего юнита, в зоне нет. ⇒ две правки этой смены (`--no-block` в `560ca20` и ответ живым именем юнита в `00d590e`) доказаны каждая на своём уровне и НЕ доказаны сквозной цепочкой; так и записано в обеих секциях «что не удалось». ⚠ Заведено строкой по её же просьбе — чтобы следующий не открывал вопрос заново и не принял «пин зелёный» за «цепочка проверена». ⭐ Отдельно ценно, что предупреждение сделано ДО сдачи, а не после: сессия назвала границу своего доказательства сама | платформа | когда-нибудь | есть стенд, проводящий свип через настоящую остановку настоящего юнита, ИЛИ записано решение, что такой стенд не строится и почему | объявленная граница платформенной сессии, 11.09 | | 404 | ⚠ **СТРОКА ПЕРЕ-ПИСАНА 11.09: её центральное утверждение было МОЕЙ ОШИБКОЙ.** Первая редакция говорила, что мутация с уехавшим якорем «не краснеет, перестаёт сажаться, и в отчёте „не посадилась“ неотличимо от „пережила“», и что механизма против этого НЕТ. **Механизм есть, и он в самом `tmmutate`:** запись с уехавшим якорем получает вердикт `rotted`, печатается отдельной строкой и **засчитывается как неожиданный исход — прогон завершается ненулевым кодом**; довод записан в самом инструменте (`backend/cmd/tmmutate/main.go`, греп `an entry whose anchor moved guards nothing`). Пере-снято мной по возражению движковой сессии, которая в этот класс наступила и была поймана СВОИМ ЖЕ прибором: `make mutations` дал `MAKE-EXIT=2 · RED 151 · SURVIVED 0 · ROTTED 1`, и протухшей оказалась ЧУЖАЯ запись, сломанная её правкой. ⛔ **ЖИВОЙ ОСТАТОК КЛАССА — не мутации, а ДОКИ И КАТАЛОГИ, где такого счётчика нет.** Линтер доков сверяет ПО СОДЕРЖИМОМУ только якоря опт-ин токен-формы; голый `файл:строка` он не видит по построению, и его протухание не даёт ни красноты, ни строки в выводе (замер 10.09: из 19 голых якорей живых секций реестра долга съехали 10, при 9 из 9 целых токен-формных). ⭐ И побочный урок, названный сессией: знаменатель проверки якорей надо брать ИЗ КАТАЛОГА, а не из своих правок — она проверила три свои записи и не проверила чужую, которую сломала | доки | скоро | у голых `файл:строка` в живых доках есть счётчик, дающий ненулевой выход, — либо они переведены в токен-форму | находка движковой сессии 11.09, пере-снята оркестратором | | 405 | **РАЗМЕН ПО СТРОКЕ 375 ИМЕЕТ НАЗВАННУЮ ЦЕНУ: проекция расходится с леджером на оборванной попытке.** Починка «обрыв не стирает уже заработанный вердикт» (заландена `3084766`) оставляет позиции прежний `cost_usd`, поэтому деньги ОБОРВАННОЙ попытки в попозиционную проекцию не добавляются: **леджер полон, проекция нет**. Названо самой движковой сессией при сдаче, а не найдено потом. ⇒ тот, кто будет сверять проекцию с леджером на книге, где были обрывы поверх отгруженных позиций, увидит расхождение и не должен принять его за потерю денег — деньги в леджере. ⚠ Заведено ОТДЕЛЬНОЙ строкой намеренно: это цена размена, который оркестратор утвердил осознанно (потеря отгруженного текста хуже неточности отчёта), и она обязана стоять рядом с ним, а не всплыть через месяц | бэкенд | когда-нибудь | попозиционная проекция и леджер сходятся на книге с обрывами поверх отгруженных позиций, либо расхождение объявлено в самой проекции | сдача движковой сессии 11.09 | -| 406 | ⛔ **КОНСИСТЕНТНОСТЬ КНИГИ НИКТО НЕ СПРАШИВАЕТ — ПРИБОРА ПО ОТГРУЖЕННОМУ ТЕКСТУ НЕТ ВОВСЕ.** Мера «сколько РАЗНЫХ передач дал один термин» ПОСТРОЕНА (`Spread`/`Variants`, `backend/internal/pipeline/mining.go:384-384`=`how many DISTINCT renderings the drafts produced`), но считается по ЧЕРНОВИКУ, на стопе майнинга, ДО редакторской волны, и читает её только человеческая таблица подписи. По ФИНАЛЬНОМУ тексту такой меры не существует ни одной. ⇒ приоритет №1 владельца («консистентные термины на всю книгу») сегодня НЕПРОВЕРЯЕМ в принципе: все четыре A/B, показавшие пользу банка, меряли ПРИСУТСТВИЕ инъектированной формы в чанке, а не отсутствие конкурирующей. **Лечение $0 и переиспользует готовое:** тот же счёт по отгружаемому тексту + строка отчёта сборки «термин X отдан тремя формами в главах 12, 340, 901». Предмет не пуст: полигонный прибор на стенде дал 13–16 % терминов с более чем одной передачей. ⚠ Это ЕДИНСТВЕННАЯ работа, которая делает качество ПРЕДЪЯВИМЫМ; без неё кривую деградации по главам снять не с чего ⛔ **ИСПОЛНЕНО ЧАСТЬЮ 11.09 ПРОГОНОМ A, и первый же результат снял мою же формулировку слепого пятна.** Прибор построен полигоном и дал по отгруженному тексту «0 терминов из 40 отданы более чем одной формой». ⚠ **Ноль держался на том, что прибор НЕ СМОТРЕЛ:** кандидаты искались регуляркой, требующей ЗАГЛАВНОЙ буквы (`[А-ЯЁ][а-яёА-ЯЁ-]+`), и склонённые строчные формы («гу-мастера», «наложницей») отпадали ДО правила сравнения. Я заказал переписать правило сравнения пословно — **заказ бил мимо**: критик применил этот рецепт к прибору на боевых данных, и не вернулся НИ ОДИН из 29 потерянных термов. Починка ГЕНЕРАЦИИ кандидатов на тех же данных даёт **48 найденных термов вместо 40** — это замер, и он стоит. ⛔ **А «шесть строк со `spread ≥ 2`» ИСПР. 11.09 — я процитировал число КРИТИКА, которое полигон ОТОЗВАЛ в том самом отчёте, что я принял актом.** Её собственный пере-замер той же починки даёт **девять**, и все девять — ШУМ: оконный поиск глотает соседние слова и считает каждое окно отдельной формой («старейшины и», «Фан не», «глава рода он»). Её формулировка дословно: «покрытие 40 → 48 — замер; счёт расхождений у починенного прибора — шум», улика — `checks/spread-candidate-fix-probe.txt`, и окну недостаёт фильтра, отвергающего лишние слова. ⇒ **числа расхождений у починенного прибора НЕТ вовсе**, и заказ следующему — сначала фильтр окна, потом счёт. ⭐ Класс моей ошибки: я взял число из отчёта, не дочитав до раздела, где его автор от него отказался. ⚠ И заголовочный «0» неустойчив к порогу складывания: под пословным правилом `沈翠` распадается на «Шэнь»/«Шэнь Цуй» и число становится 1. ⇒ остаток ряда: прибор по ФИНАЛЬНОМУ тексту с (а) генерацией кандидатов без регистрового фильтра, (б) названным порогом складывания, (в) замеренной чувствительностью к нему. ⭐ **ПРАВИЛО ВЫБОРА ПЕРВИЧНОЙ КОЛОНКИ — ратифицируется актом приёмки пака 11.09; записано здесь, чтобы пережило смену, и УЖЕ ИСПРАВЛЕНО ДВАЖДЫ.** У прибора две колонки: СТРОГАЯ (равенство стеммов, как у пост-проверки) и ЯКОРНАЯ (признаёт склонённую передачу). **Первичная — ЯКОРНАЯ, строгая идёт полом точности рядом.** ⛔ **Направления ошибок, иначе довод читается наоборот:** строгая ошибается в сторону ЛОЖНОЙ ТРЕВОГИ (форма доехала склонённой, а она объявляет нарушение); якорная — в сторону СОКРЫТИЯ (засчитывает совпадение, которого по смыслу нет). **Как детекторы нарушения на прогоне A:** строгая дала 18 сообщений, настоящих 7 ⇒ точность **0.39**; якорная дала **9** при 7 настоящих ⇒ **0.78**. ⚠ **Число ИСПР. 11.09 и было 1.00 ДО ужесточения `NearStems` до строгого префикса** — ужесточение вернуло `邪魔` («злой»/«злог» — замена, не усечение) в ложные, и это цена размена, которым закрыты три коллизии многословных передач. Вторая ложная — `话事人`, однословная передача, якорить нечем. ⇒ **каждое пятое сообщение первичной колонки ложное, и читать её надо с этой поправкой.** ⇒ основание НЕ «ложная тревога дешевле сокрытия» (так было написано, и так оно аргументирует ОБРАТНОЕ), а такое: **детектор, врущий в 61 % сообщений, не громче — его перестают читать, а непрочитанный прибор не защищает ничего.** ⛔ **Второе основание, от данных не зависящее:** писатели `decl.forms` в не-тестовом Go только сидовые (`backend/internal/seed/normalize.go:85`=`d := Decl{Invariant: t.Decl.Invariant`, `backend/internal/membank/memseed.go:166`=`b, mErr := json.Marshal(declInfo{`), майнер форм не пишет, а дверь `BankCorrection` контракта поля `decl`/`forms` не несёт вовсе ⇒ на дорожке продукта форм нет НИ У КОГО, и строгая колонка там вырождается в одношаговое равенство. ⚠ Иллюстрация «0 форм из 69 в банках прогона» основанием НЕ является. ⛔ **ДВА ЧИСЛА ТОЧНОСТИ, путать нельзя (замерено зоной 11.09): само ОТНОШЕНИЕ строгого префикса — 17 из 30 равномерной выборки принятых пар, ≈0.57** (`рода~родной`, `гор~горная`, `так~также` — разные леммы); **колонка в своих ограждениях — 11 из 11 на прогоне A.** ⇒ работает не отношение, а ограждения (якорь · многословное окно · границы предложения), и их эффективность замерена на ОДНОЙ книге. Названный отказ: где слово банковской передачи окажется соседом другого слова в той же позиции окна, колонка соврёт в сторону сокрытия. ⛔⛔ **ПРАВИЛО ДЕЙСТВУЕТ ПРИ УСЛОВИИ: пока прибор ФЛАГУЕТ и не отказывает.** Станет он гейтом отгрузки — ложная тревога начнёт стоить удержанного текста или пере-генерации, то есть ДЕНЕГ, асимметрия перевернётся, и порядок колонок надо решать заново. ⇒ **триггеров пере-открытия ДВА:** (1) появился МАШИННЫЙ писатель `decl.forms`; (2) **эта колонка стала гейтом** — носители: `Gates.Glossary.PostcheckGate` построен (`backend/internal/pipeline/quality.go:361`=`gateOn := r.Pipeline.Gates.Glossary.PostcheckGate`, свёрнут в снапшот) и выставлен в НУЛЕ из четырёх живых конфигов, плюс ряд **296** (точечная перегенерация по промаху — направление владельца, работы нет). ⚠ Второй триггер сегодня БЛИЖЕ первого. ⚠ Прежнее моё решение (первичная — строгая) принято и отменено 11.09 в один день; цена разворота — согласие с ручным оракулом 13/16 против 10/16, **и это n=16: направление, а не величина.** Так же и 0.57 — порядок, а не третья цифра.| бэкенд | **скоро** | счёт расхождения форм по отгруженному тексту + строка отчёта сборки | аудит банка 11.09, `research/34` §2.1 | +| 406 | ⛔ **КОНСИСТЕНТНОСТЬ КНИГИ НИКТО НЕ СПРАШИВАЕТ — ПРИБОРА ПО ОТГРУЖЕННОМУ ТЕКСТУ НЕТ ВОВСЕ.** Мера «сколько РАЗНЫХ передач дал один термин» ПОСТРОЕНА (`Spread`/`Variants`, `backend/internal/pipeline/mining.go:391`=`how many DISTINCT renderings the drafts produced`), но считается по ЧЕРНОВИКУ, на стопе майнинга, ДО редакторской волны, и читает её только человеческая таблица подписи. По ФИНАЛЬНОМУ тексту такой меры не существует ни одной. ⇒ приоритет №1 владельца («консистентные термины на всю книгу») сегодня НЕПРОВЕРЯЕМ в принципе: все четыре A/B, показавшие пользу банка, меряли ПРИСУТСТВИЕ инъектированной формы в чанке, а не отсутствие конкурирующей. **Лечение $0 и переиспользует готовое:** тот же счёт по отгружаемому тексту + строка отчёта сборки «термин X отдан тремя формами в главах 12, 340, 901». Предмет не пуст: полигонный прибор на стенде дал 13–16 % терминов с более чем одной передачей. ⚠ Это ЕДИНСТВЕННАЯ работа, которая делает качество ПРЕДЪЯВИМЫМ; без неё кривую деградации по главам снять не с чего ⛔ **ИСПОЛНЕНО ЧАСТЬЮ 11.09 ПРОГОНОМ A, и первый же результат снял мою же формулировку слепого пятна.** Прибор построен полигоном и дал по отгруженному тексту «0 терминов из 40 отданы более чем одной формой». ⚠ **Ноль держался на том, что прибор НЕ СМОТРЕЛ:** кандидаты искались регуляркой, требующей ЗАГЛАВНОЙ буквы (`[А-ЯЁ][а-яёА-ЯЁ-]+`), и склонённые строчные формы («гу-мастера», «наложницей») отпадали ДО правила сравнения. Я заказал переписать правило сравнения пословно — **заказ бил мимо**: критик применил этот рецепт к прибору на боевых данных, и не вернулся НИ ОДИН из 29 потерянных термов. Починка ГЕНЕРАЦИИ кандидатов на тех же данных даёт **48 найденных термов вместо 40** — это замер, и он стоит. ⛔ **А «шесть строк со `spread ≥ 2`» ИСПР. 11.09 — я процитировал число КРИТИКА, которое полигон ОТОЗВАЛ в том самом отчёте, что я принял актом.** Её собственный пере-замер той же починки даёт **девять**, и все девять — ШУМ: оконный поиск глотает соседние слова и считает каждое окно отдельной формой («старейшины и», «Фан не», «глава рода он»). Её формулировка дословно: «покрытие 40 → 48 — замер; счёт расхождений у починенного прибора — шум», улика — `checks/spread-candidate-fix-probe.txt`, и окну недостаёт фильтра, отвергающего лишние слова. ⇒ **числа расхождений у починенного прибора НЕТ вовсе**, и заказ следующему — сначала фильтр окна, потом счёт. ⭐ Класс моей ошибки: я взял число из отчёта, не дочитав до раздела, где его автор от него отказался. ⚠ И заголовочный «0» неустойчив к порогу складывания: под пословным правилом `沈翠` распадается на «Шэнь»/«Шэнь Цуй» и число становится 1. ⇒ остаток ряда: прибор по ФИНАЛЬНОМУ тексту с (а) генерацией кандидатов без регистрового фильтра, (б) названным порогом складывания, (в) замеренной чувствительностью к нему. ⭐ **ПРАВИЛО ВЫБОРА ПЕРВИЧНОЙ КОЛОНКИ — ратифицируется актом приёмки пака 11.09; записано здесь, чтобы пережило смену, и УЖЕ ИСПРАВЛЕНО ДВАЖДЫ.** У прибора две колонки: СТРОГАЯ (равенство стеммов, как у пост-проверки) и ЯКОРНАЯ (признаёт склонённую передачу). **Первичная — ЯКОРНАЯ, строгая идёт полом точности рядом.** ⛔ **Направления ошибок, иначе довод читается наоборот:** строгая ошибается в сторону ЛОЖНОЙ ТРЕВОГИ (форма доехала склонённой, а она объявляет нарушение); якорная — в сторону СОКРЫТИЯ (засчитывает совпадение, которого по смыслу нет). **Как детекторы нарушения на прогоне A:** строгая дала 18 сообщений, настоящих 7 ⇒ точность **0.39**; якорная дала **9** при 7 настоящих ⇒ **0.78**. ⚠ **Число ИСПР. 11.09 и было 1.00 ДО ужесточения `NearStems` до строгого префикса** — ужесточение вернуло `邪魔` («злой»/«злог» — замена, не усечение) в ложные, и это цена размена, которым закрыты три коллизии многословных передач. Вторая ложная — `话事人`, однословная передача, якорить нечем. ⇒ **каждое пятое сообщение первичной колонки ложное, и читать её надо с этой поправкой.** ⇒ основание НЕ «ложная тревога дешевле сокрытия» (так было написано, и так оно аргументирует ОБРАТНОЕ), а такое: **детектор, врущий в 61 % сообщений, не громче — его перестают читать, а непрочитанный прибор не защищает ничего.** ⛔ **Второе основание, от данных не зависящее:** писатели `decl.forms` в не-тестовом Go только сидовые (`backend/internal/seed/normalize.go:85`=`d := Decl{Invariant: t.Decl.Invariant`, `backend/internal/membank/memseed.go:166`=`b, mErr := json.Marshal(declInfo{`), майнер форм не пишет, а дверь `BankCorrection` контракта поля `decl`/`forms` не несёт вовсе ⇒ на дорожке продукта форм нет НИ У КОГО, и строгая колонка там вырождается в одношаговое равенство. ⚠ Иллюстрация «0 форм из 69 в банках прогона» основанием НЕ является. ⛔ **ДВА ЧИСЛА ТОЧНОСТИ, путать нельзя (замерено зоной 11.09): само ОТНОШЕНИЕ строгого префикса — 17 из 30 равномерной выборки принятых пар, ≈0.57** (`рода~родной`, `гор~горная`, `так~также` — разные леммы); **колонка в своих ограждениях — 11 из 11 на прогоне A.** ⇒ работает не отношение, а ограждения (якорь · многословное окно · границы предложения), и их эффективность замерена на ОДНОЙ книге. Названный отказ: где слово банковской передачи окажется соседом другого слова в той же позиции окна, колонка соврёт в сторону сокрытия. ⛔⛔ **ПРАВИЛО ДЕЙСТВУЕТ ПРИ УСЛОВИИ: пока прибор ФЛАГУЕТ и не отказывает.** Станет он гейтом отгрузки — ложная тревога начнёт стоить удержанного текста или пере-генерации, то есть ДЕНЕГ, асимметрия перевернётся, и порядок колонок надо решать заново. ⇒ **триггеров пере-открытия ДВА:** (1) появился МАШИННЫЙ писатель `decl.forms`; (2) **эта колонка стала гейтом** — носители: `Gates.Glossary.PostcheckGate` построен (`backend/internal/pipeline/quality.go:361`=`gateOn := r.Pipeline.Gates.Glossary.PostcheckGate`, свёрнут в снапшот) и выставлен в НУЛЕ из четырёх живых конфигов, плюс ряд **296** (точечная перегенерация по промаху — направление владельца, работы нет). ⚠ Второй триггер сегодня БЛИЖЕ первого. ⚠ Прежнее моё решение (первичная — строгая) принято и отменено 11.09 в один день; цена разворота — согласие с ручным оракулом 13/16 против 10/16, **и это n=16: направление, а не величина.** Так же и 0.57 — порядок, а не третья цифра.| бэкенд | **скоро** | счёт расхождения форм по отгруженному тексту + строка отчёта сборки | аудит банка 11.09, `research/34` §2.1 | | 407 | **ПОСТ-ПРОВЕРКА БАНКА СПРАШИВАЕТ НЕ ТО: присутствие формы ГДЕ-НИБУДЬ в чанке вместо формы НА МЕСТЕ срабатывания, и про конкурирующую передачу не спрашивает никогда.** `backend/internal/membank/mempostcheck.go` — вопрос bag-of-words по всему чанку ⇒ дрейф ВНУТРИ составного имени невидим по построению. Замерено исполнением на копии: ряд `方源`→«Фан Юань» с алиасом `古月方源`, выход «Гу Юэ Фан Юань» даёт 0 промахов при подписанном каноне `古月 → Гуюэ`; контроль рядом (форма реально отсутствует) даёт 1 промах, то есть прибор спрошен и отвечает. ⚠ **У экземпляра ДВА корня, и смена вопроса закрывает только один:** ряд `古月` съеден равнотрастовым длиннейшим матчем (`memory.go` `suppressContained`) МОЛЧА. Дешёвая половина — проверять алиас-ключ на СОБСТВЕННУЮ канонную форму (день); дорогая (детектор значения по исходнику) — ряд **286**, загейчен замером. ⚠ Класс объявлен в дереве с 06.09 (`experiments/23-editor-tier.md` §Д50.3) — новизна нулевая, ряд заводится как НОСИТЕЛЬ, которого у него не было ⛔ **ИСПР. 11.09 ПО СЛОВУ ВЛАДЕЛЬЦА — РЯД БЫЛ НАПИСАН ПО НЕСУЩЕСТВУЮЩЕЙ ОСИ.** Слово: «нет такого понятия [подписанный термин], подписан либо ВЕСЬ банк, либо он в неконсистентном состоянии; есть вариант поставить галочку скипать подпись с фронта — тогда автоподписывание». Это ратифицировано `D39.144`: подписывается банк ЦЕЛИКОМ, пер-термная подпись прямо названа НЕ моделью продукта, дефолт движка — авто-продолжение с неподписанным банком, строки едут С ПОМЕТКОЙ. ⇒ **настоящий дефект глубже, чем «прибор не видит дрейфа»: в коде живёт пер-термный автомат из трёх состояний (`auto`, `draft`, `approved`) (`backend/internal/store/migrate.go:192`=`term status machine`), и пост-проверка считает нарушением ТОЛЬКО `approved` — то есть принимает решение по ЕДИНИЦЕ, КОТОРОЙ В ПРОДУКТОВОЙ МОДЕЛИ НЕТ.** Это и есть причина нуля при 18 найденных (строка **419**): сломана не поверхность, а ОСЬ решения. Закон `D39.104` на проводе один для всех строк; состояние книги — «подписана целиком» либо «авто-продолжение», и мерить надо им. | бэкенд | скоро | вопрос пост-проверки + видимость подавления вложенного ряда | аудит банка 11.09, находка `K3` (единственная пережившая верификацию) | | 408 | **ДАННЫЕ БАНКА НЕ ДОСЕЯНЫ, И ИМЕННО ИЗ-ЗА ЭТОГО ГЕЙТ КОНСИСТЕНТНОСТИ НЕЛЬЗЯ ВКЛЮЧИТЬ.** ⛔ **ЧИСЛО ИСПР. 11.09 — ПРЕЖНЕЕ БЫЛО ДО-СТЕММЕРНОЙ БАЗОЙ, а ряд гейтил им решение владельца.** Пере-снято мною прогоном пинов размеченного корпуса (`TM_CHECKER_LABELS=1 go test ./internal/membank/ -run TestK6 -v`): **база (`metrics.json`, до стеммера и до #10) — tp=1 fp=14 ⇒ 0.0667**, и это то, что здесь стояло; **ОТГРУЗОЧНЫЙ путь СЕГОДНЯ — tp=1 fp=6 tn=251 ⇒ 0.1429**; со стеммизацией `decl`-форм — **fp=0 ⇒ 1.0000**. ⚠ И концентрация названа неверно: класса `转` на отгрузочном пути НЕТ ВОВСЕ (его снял стеммер), остаточные шесть — это `元石` ×2 и `蛊虫` ×4, то есть **ровно склонительный разрыв, лечащийся досевом форм**. ⇒ направление ряда верно, величина отставала на два улучшения, а список ложных описывал уже не тот прибор. ⭐ **И это прямо смыкает ряд с 423:** стеммизация `decl`-форм уводит точность в 1.0000, а `decl`-форм нет ни в одной машинной выгрузке — то есть «досеять данные» и есть лечение, но на дорожке продукта досевать некому. Три дыры, все в ДАННЫХ, не в Go: (а) глагольные и многословные термины засеяны с пустыми формами — один `炼化` дал 7 ложных промахов (⚠ **испр. 11.09: это число из ДРУГОГО замера** — `docs/experiments/23-editor-tier.md:7630`=`Все семь «промахов»`, не из `labels/metrics.json`; в размеченном корпусе `炼化` нет вовсе, и обе величины верны порознь, но ссылка была одна на две); (б) односимвольный ханьский ключ структурно вне автомата без `allow_short` (порог 2 знака, `backend/internal/membank/memory.go:72`=`minKeyLenHan = 2`), а это самый частый класс канона в китайском ранобэ (蛊, 道, 气); (в) сид-линт об этом молчит. ⚠ Владелец 05.09 отложил гейт до замера ложных срабатываний («если он ложно положительно будет стрелять — это плохо, очень плохо») — вот их источник, и он лечится досевом, а не кодом ⚠ **11.09: это один из ТРЁХ инстансов общего класса — ряд 423** (майнер эмитит только своё поле; на немайненном корпусе меры вырождаются). | бэкенд+полигон | **скоро** | досев форм · `allow_short` у односимвольных · линт сида, который об этом кричит | аудит банка 11.09, `research/34` §2.3 | | 409 | **РЕДАКТОР — ИСТОЧНИК РАЗНОБОЯ, А НЕ СТРАХОВКА ПОВЕРХ БАНКА, и это замерено дважды.** Без закон-блока редактор ломает 5–12 из 20 уже ВЕРНЫХ клеток черновика (`experiments/18-editor-wire-probe.md` §A.1); с подключённым банком боевой редактор держит ХУДШЕЕ покрытие канона в панели — 0.882 против 0.909 у черновика, который он правит (`experiments/22` §6.2, 1124 упоминания). Два механических носителя: черновая волна выбирает по БАЗОВОМУ банку с исключением намайненного (`bankmaterialize.go:329`) ⇒ на первом прогоне книги черновик идёт вообще без добытого банка; редакторская волна, единственная с полным банком, теряет липкость МЕЖДУ правочными единицами внутри главы (`waverun.go:717`) при **86 % многоединичных глав (1962 из 2283)**. ⇒ закон-блок обязан стать инвариантом конфига, а не следствием выбора модели редактора. ⚠ Инцидентов дрейфа от потери липкости — НОЛЬ наблюдений, замерена только экспозиция: правка без замера была бы стрельбой вслепую. ⛔ **ИСПР. 11.09: «замер снимается прогоном A» назначал работу ЗАКРЫТОМУ прогону.** Прогон A принят актом `D39.247`, зона полигона выведена из скоупа целиком (`D39.248` п.4) — ждать этого замера больше не от кого. **Что прогон A реально дал по предмету — ЭКСПОЗИЦИЯ на живой книге и совпадение при N=1:** манифест дал `docs/experiments/24-door-to-file.md:815`=`units_total 2 / chunks 2` (⚠ испр. 17.09: было :807) у главы 1 при `units_total 1 / chunks 2` у глав 2 и 3, то есть **одна многоединичная глава из трёх**, и единственный термин книги, отданный ДВУМЯ формами (`开窍大典`), стоит обеими формами в ТОЙ ЖЕ главе 1 (`docs/experiments/24-door-to-file.md:334`=`ДВЕ формы, и обе в одной главе` (⚠ испр. 17.09: было :329)). ⚠ **Это наблюдение, а не доказательство:** атрибуция в отчёте доведена до ГЛАВЫ, а не до правочной ЕДИНИЦЫ — переход разнобоя ЧЕРЕЗ границу единицы не показан, и N=1. ⇒ по-юнитный замер дрейфа несёт БЭКЕНД на боевой книге, а решение по липкости принимается ПО ЕГО ИТОГУ: закрытие прогона A убрало исполнителя, а не требование | бэкенд | скоро | закон-блок инвариантом конфига + по-юнитный замер дрейфа на боевой книге и решение по липкости между правочными единицами по его итогу (прогона A ждать нечего — `D39.247`) | аудит банка 11.09, `research/34` §2.4 | diff --git a/docs/DOC_CLEANUP_PLAN.md b/docs/DOC_CLEANUP_PLAN.md index a26fee43..d8ac6db2 100644 --- a/docs/DOC_CLEANUP_PLAN.md +++ b/docs/DOC_CLEANUP_PLAN.md @@ -138,7 +138,7 @@ **Б7 — Шов и эмиттер 16, 17** Находки: N042, N043, N044, N045, N046, N047, N048, N049, N115, N116, N117 -**Приёмка:** N046 — форма якоря `mining.go:243-244`=`re-seed the bank with the auto rows`; N047 — атрибуция D39.159 п.7 и пере-суд «строки 139»; N048 — ОБЕ строки таблицы (97 и 103); N049 — хвост с `PD-427`; N044/N115 — StreamVersion **1.2** ⚠ (испр. 05.09: на HEAD она **1.3** — минор приехал лендингом `81a89e9`; критерий читать как «версия записана ВЕРНО», а не как литерал `1.2`); N045 — двенадцать якорей в токен-форме. (Прежний критерий про N032 снят: N032 живёт в Б6, испр. по замечанию контролёра.) §5–§6 дока 16 сам себя баннирует как отработанный хендофф, внешних ссылок ноль — кандидат на вынос в `archive/reports/` с указателем (решение объявить, не делать молча). +**Приёмка:** N046 — форма якоря `backend/internal/pipeline/mining.go:246-246`=`re-seed the bank with the auto rows`; N047 — атрибуция D39.159 п.7 и пере-суд «строки 139»; N048 — ОБЕ строки таблицы (97 и 103); N049 — хвост с `PD-427`; N044/N115 — StreamVersion **1.2** ⚠ (испр. 05.09: на HEAD она **1.3** — минор приехал лендингом `81a89e9`; критерий читать как «версия записана ВЕРНО», а не как литерал `1.2`); N045 — двенадцать якорей в токен-форме. (Прежний критерий про N032 снят: N032 живёт в Б6, испр. по замечанию контролёра.) §5–§6 дока 16 сам себя баннирует как отработанный хендофф, внешних ссылок ноль — кандидат на вынос в `archive/reports/` с указателем (решение объявить, не делать молча). **Б8 — Движковые арх-доки 03, 09, 10, 12** diff --git a/docs/NEAR_TERM_PLAN.md b/docs/NEAR_TERM_PLAN.md index b3892f78..9e035e33 100644 --- a/docs/NEAR_TERM_PLAN.md +++ b/docs/NEAR_TERM_PLAN.md @@ -56,7 +56,7 @@ | 8 | **бэкенд/деньги** | ⛔ **«ПАДЕНИЕ, КОТОРОЕ ВИДНО»** — четверть COGS в одном механизме (**415**), слепые приборы отказа (**414**, **422**), молчащая подстановка цены. ⚠ Пин цены **413** из пака ВЫНУТ: это данные и отдельное решение | пак, $0 | ✅ **ЗАКРЫТ АКТОМ `D39.251`** (испр. 15.09; дальше — запись 11.09) · ✍️ **НАПИСАН 11.09** — `BACKEND_FAILURE_YOU_CAN_SEE_SESSION_PROMPT.md`; выдаётся СВЕЖЕЙ сессии сразу по закрытии 4 ⛔ **КРИТЕРИЙ ПРИЁМКИ ПАКА — МЕХАНИЗМ, А НЕ ПРОПОРЦИЯ** (поправка старшего коллеги 11.09): лекарство есть распространение ратифицированного образца `D2` п.3 на вторую причину, и оно верно при 25 % и при 5 % — пропорция задаёт очерёдность, не форму. ⇒ судить по строкам «ни одного удвоения без вердикта о вырождении; каждая выброшенная попытка — строкой леджера с причиной», а не по «с 25.4 % до X». Значит прогон B пак не гейтит и паком не гейтится.| | 9 | **платформа+бэкенд** | ⭐ **ХОЛОДНЫЙ ПРОГОН B — платформа → бэкенд → файл, ВТОРОЙ раз.** Уточнение владельца 11.09: спрашивая «перепрогоны нужны», он имел в виду именно сквозной прогон, а не пере-запуск тестов. Покупает ЧЕТЫРЕ вещи, которых нет ни у кого: **(1)** миграция **v17** (`wave_selection`) пака консистентности ни разу не шла на живом прогоне — только в тестах; **(2)** прибор консистентности впервые даст числа с ЖИВОГО прогона, а не с пере-скана сохранённой базы прогона A — сегодня все `I1`/`I2` получены повторным чтением одних и тех же данных; **(3)** платформенный пак (идемпотентный резюм, терминальное состояние) проверяется сквозь, а не юнитами; **(4)** ⛔ **вторая точка по денежной дыре, и она нужна по норме, а не для красоты** — весь довод «четверть денег купила пустоту» стоит на ЧЕТЫРЁХ одиночных розыгрышах, а квирки §3д прямо говорят: смета DeepSeek с одиночного вызова может ошибиться на порядок, сметы строятся на ПОВТОРАХ. ⇒ у числа 25.4 % есть знак и нет величины, пока прогон не повторён. **Цена ≈$0.42 по образцу прогона A, машинный кап ≈$2.5.** ⛔ **ПРЕ-РЕГИСТРАЦИЯ ЗНАМЕНАТЕЛЯ, иначе B ответит числом на другой вопрос** (поправка старшего коллеги 11.09): B — ЕЩЁ ОДИН одиночный прогон, и две точки распределением не станут. Настоящая ценность B не «вторая точка к 25.4 %», а `request_log` ВСЕХ единиц прогона ⇒ **доля выброшенных первых попыток ПО ЕДИНИЦАМ, а не по вызовам.** Это и пре-регистрируется. ⚠ И идёт B **ПАРАЛЛЕЛЬНО** паку о падениях, а не после: они в РАЗНЫХ слотах (бэкенд против платформы), и правило «один промт на зону» их не сталкивает | ≈$0.42 | ✅ **ЗАКРЫТ АКТОМ `D39.252`**, факт $0.396657 | | 10 | **бэкенд** | ✅ **ЗАКРЫТ АКТОМ `D39.266`** 17.09 (`d25e418`, 32 пути, $0): носитель — таблица `bank_basis`, миграция v19, аддитивная; ряд **490** закрыт по трём условиям, **130** получил ТРЕТИЙ исход (замысел достигнут другим механизмом, стоп-решение остаётся за владельцем), два отказа с замером, заведён **496**. Числа приёмки: кампания **315/315 поймано, выживших 0**; M1 — **67 из 69** и **64 из 66** обслужено из памяти. Прежняя формулировка предмета: ⛔ **БАНК: РЕШЁННОЕ ОСТАЁТСЯ РЕШЁННЫМ** — предмет пере-определён проверкой 15.09. Пак 12.09 «в банк по разнобою» (`archive/prompts/BACKEND_WHAT_THE_BANK_IS_MADE_OF_SESSION_PROMPT.md`) снят баннером: все термы со `spread ≥ 2` уже в банке (A 9 из 9, B 8 из 8). Настоящий дефект приоритета №1 — пере-решение банка терминологом: на подписи замерено (у неподписанных строк сменились передачи A 12 из 69, B 14 из 66), на каждой следующей покупке глав выведено из кода — отгруженные главы по порядку слотов остаются со старыми передачами (`backend/internal/pipeline/volume.go`, греп `DELIVERY BEFORE REWORK`). Ряды 130 · 330 · 438 · 440 | $0 + центы перекупки банк-ролей | ⛔ **ПРОЕКТ ОРКЕСТРАТОРА ОТОЗВАН 15.09.** «Банк — журнал решений с окнами» (две редакции Fable 5) расходился с 12 пунктами решённого, замеренного и этого же плана и опирался примерно на десятую часть относящегося; улики — `archive/reports/BANK_CONSILIUM_INPUTS_2026-09-15/`. ✅ **КОНСИЛИУМ ОТРАБОТАЛ, акт `D39.253` (16.09).** Итог — `research/35-bank-memory-consilium.md`: банк как решение по уликам с пятью событиями пересмотра, N-1 на построенном фильтре, N-3 на сборке вида, ряды бэклога 441–450. ✅ **СЛОВО ВЛАДЕЛЬЦА ПОЛУЧЕНО 16.09** (`D39.254`): держать решение между покупками — да · при невозможной безродовой форме — перестроить фразу, иначе «как лучше читается» · богаче контекст — да, меру решает замер M4 · голос и ты↔вы — строить. ⚠ **Вопрос об отказе терминолога ПЕРЕ-ОПРЕДЕЛЁН владельцем:** у банк-ролей нет ни ретрая, ни фоллбека (ряды 435, 438), и он считает это дырой ⇒ заведён ряд 451 «умный участок в точке банка», ряд 452 — голос. ✅ **ГРАНИЦЫ ДАНЫ ВЛАДЕЛЬЦЕМ 16.09** — «в границах ратифицированного», и слот бэкенда освобождён приёмкой `D39.255`. ✅ **ПАК 1 ПРИНЯТ 16.09 актом `D39.257`** (29 путей, $0; посылка В1 закрыта замером — 25 из 26 при идентичном входе). Промт написан был 16.09 — `BACKEND_BANK_ZERO_LAYER_SESSION_PROMPT.md`, пак $0 (громкий отказ при цели без инъекционных текстов до первой траты · отказ роли перестаёт ехать законом · предикат «спорный» как ДВА предиката с заданными знаменателями · носитель посылки «новой улики не было»); круг опровергателя пройден. ✅ **ВЫДАН и ПРИНЯТ 16.09** — акт `D39.257`. Вопросы владельцу, в том числе ряд 130 (`research/34` §4 п.4), ставит его итог; он же решает порядок относительно 5(в). Довод «перекрой глав без якоря пере-решил бы банк платной книги» остаётся входом ✅ **ПЕРЕНОС БАНКА РЕШЁН ВЛАДЕЛЬЦЕМ 16.09 — акт `D39.256`:** в КНИГУ-ПРОДОЛЖЕНИЕ едут ПОДПИСАННЫЕ строки ПЛЮС строки, РЕШЁННЫЕ ДВИЖКОМ; стройка ДВУХЗОННАЯ и ещё не начата — ряд **461**. ⚠ **Эррата 16.09-б:** формулировка «причина смен передачи — якорь» в `D39.256` и `D39.257` СИЛЬНЕЕ замера — измерено лишь отсутствие новой улики во ВХОДЕ, причина остаётся гипотезой, гейт — ряд **466**. Прочее живое банка: ряды **462** · **465**. ⭐ **ЗАКАЗАН 17.09: промт «РЕШЁННОЕ НЕ ПЕРЕ-СПРАШИВАЕТСЯ» ВЫДАН** сессии `textmachine-main-57` (выдача №14, `docs/BACKEND_SETTLED_STAYS_SETTLED_SESSION_PROMPT.md`, $0). Строит базис решённого как ИСТОЧНИК ОТВЕТОВ (ряд 130) и стража разрыва окон (ряд 490); замер M1 идёт ПЕРВЫМ блоком, и носитель не пишется, пока оркестратор не принял форму предиката по числам. ⚠ **Состав сузился против проекта консилиума, и это измерено, а не решено вкусом:** N-4 и Б-5 ПОСТРОЕНЫ паком `D39.257`; N-6 гейчен разморозкой фронта (ряды **214** · **221**); Б-2 протух — токен «stops loudly» живёт только в тесте; N-3 и стратифицированный отбор вынесены НАРУЖУ, каждый со своим замером. Промт прошёл два рубежа (механическая сверка 13 блоков и субагент-опровергатель: 12 находок, 9 денежных, все внесены) и всё равно был исправлен релеем после выдачи — материал замера указывал на другую книгу. | -| 11 | **бэкенд** | ⭐ **ОКНО ПЕРЕ-КРОЯ — ВСЁ, ЧТО ДВИГАЕТ НАРЕЗКУ И ДАННЫЕ ПАРЫ, ЕДЕТ ДО СТАРТА ХОЛОДНОГО ПРОГОНА.** Открыто 18.09 покупкой прогона B, закроется стартом прогона — см. 5(в). Состав: **283** + **303** (один шов с двух сторон: грамматика не участвует там, где режут) · **201** (движковое «Глава N» внутри текста) · **410** (голос построен сквозь и выключен двумя тумблерами, конфиг-ключа нет) · вынос свёрток нормализации источника из ядра в данные пары. ⛔ **Порядок задан владельцем 18.09, дословно: «цель: не бежать поскорее приступить к холодному прогону а подготовить бэкенд так что б этот холодный прогон исполнить максимально правильно. Сколько паков и бэклога нам для этого нужно закрыть не очень важно. Халтурить не надо, подгонять тоже ничего не надо, спиливаем долг из бэкенда, реализуем актульный, нужный чистый архитектурно код».** ⇒ число паков перестало быть ограничением, и «успеть к прогону» перестало быть доводом за срез | паки, $0 | ✍️ **ПАК 1 НАПИСАН 18.09** — `BACKEND_CHAPTER_STRUCTURE_SESSION_PROMPT.md` (границы глав; корень найден подсказкой владельца — см. «СОСТОЯНИЕ НА 18.09»). Не выдан: идёт второй рубеж | +| 11 | **бэкенд** | ⭐ **ОКНО ПЕРЕ-КРОЯ — ВСЁ, ЧТО ДВИГАЕТ НАРЕЗКУ И ДАННЫЕ ПАРЫ, ЕДЕТ ДО СТАРТА ХОЛОДНОГО ПРОГОНА.** Открыто 18.09 покупкой прогона B, закроется стартом прогона — см. 5(в). Состав: **283** + **303** (родственные: грамматика не участвует там, где решают границу; пак 2 по 303 снимает запрет `D39.224` п.7) · **201** (движковое «Глава N» внутри текста) · **410** (голос построен сквозь и выключен двумя тумблерами, конфиг-ключа нет) · вынос свёрток нормализации источника из ядра в данные пары. ⛔ **Порядок задан владельцем 18.09, дословно: «цель: не бежать поскорее приступить к холодному прогону а подготовить бэкенд так что б этот холодный прогон исполнить максимально правильно. Сколько паков и бэклога нам для этого нужно закрыть не очень важно. Халтурить не надо, подгонять тоже ничего не надо, спиливаем долг из бэкенда, реализуем актульный, нужный чистый архитектурно код».** ⇒ число паков перестало быть ограничением, и «успеть к прогону» перестало быть доводом за срез | паки, $0 | ✍️ **ПАК 1 НАПИСАН 18.09** — `BACKEND_CHAPTER_STRUCTURE_SESSION_PROMPT.md` (границы глав; корень найден подсказкой владельца — см. «СОСТОЯНИЕ НА 18.09»). Не выдан: идёт второй рубеж | ⛔ **ПОЧЕМУ 4 ВСТАЛ ПЕРЕД 5 (решение 11.09 по мерилу владельца).** Мерило — «качественный КОНСИСТЕНТНЫЙ перевод длинных книг». Аудит банка 11.09 показал, что **книжного вопроса о консистентности не задаёт ни один @@ -88,8 +88,8 @@ двух**; на EPUB границу единолично назначает навигация издателя, и когда она врёт — а в книге прогона 60 целей из 71 имеют тело, байт-в-байт равное собственному заголовку, — её никто не поправляет. -**Следствие для бэклога, и оно сокращает работу, а не добавляет:** ряды **283** и **303** — один и тот же шов -с двух сторон. 303 говорит «чанкер безъюнитную грамматику уже умеет, ингест по ней не режет», 283 — «ридер +**Следствие для бэклога, и оно сокращает работу, а не добавляет:** ряды **283** и **303** — РОДСТВЕННЫЕ, но не один шов +(испр. после круга опровержения: 303 — резак ингеста, 283 — билдер выдачи). 303 говорит «чанкер безъюнитную грамматику уже умеет, ингест по ней не режет», 283 — «ридер делает главу из каждого документа». Оба лечатся тем, что грамматика начинает участвовать ТАМ, ГДЕ РЕЖУТ, и ни один не требует ни порога в знаках, ни ветки по языку. ⚠ Ряд **302** (несколько глав в ОДНОМ документе) — ОБРАТНЫЙ предмет и в это окно намеренно не берётся. diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index fc2c019b..6304f3cd 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1747,13 +1747,13 @@ langpacks — **0 файлов** при **16** прочитанных прибо | читатель | адрес | что получает | |---|---|---| -| `attachConsolidatedDst` → режим эмиссии | `backend/internal/pipeline/terminologist.go:1415`=`Terms the role did not answer are left untouched` | передачу ⇒ `draft` вместо отката к догадке куска | -| `attachClassifiedType` / `attachClassifiedGender` | `backend/internal/pipeline/terminologist.go:1430`=`attachClassifiedGender stamps` | тип и род из базиса | -| `consolidatedRows` → `ConsolidationKeyConflicts` | `backend/internal/pipeline/terminologist.go:747`=`membank.ConsolidationKeyConflicts(consolidatedRows(cands, out), bank)` | судится, см. решение ниже | -| `CanonConflicts` | `backend/internal/pipeline/terminologist.go:725`=`terminology.CanonConflicts(cands, out, canon)` | судится, см. решение ниже | +| `attachConsolidatedDst` → режим эмиссии | `backend/internal/pipeline/terminologist.go:1418`=`Terms the role did not answer are left untouched` | передачу ⇒ `draft` вместо отката к догадке куска | +| `attachClassifiedType` / `attachClassifiedGender` | `backend/internal/pipeline/terminologist.go:1433`=`attachClassifiedGender stamps` | тип и род из базиса | +| `consolidatedRows` → `ConsolidationKeyConflicts` | `backend/internal/pipeline/terminologist.go:750`=`membank.ConsolidationKeyConflicts(consolidatedRows(cands, out), bank)` | судится, см. решение ниже | +| `CanonConflicts` | `backend/internal/pipeline/terminologist.go:728`=`terminology.CanonConflicts(cands, out, canon)` | судится, см. решение ниже | | `ConsolidationConflicts` (§G2, свои против своих) | рядом, тот же цикл | судится | | `labelRows` → `TypeLabelMismatches` | там же | судится | -| счётчики `Consolidated`/`Declined`/`Unanswered` | `backend/internal/pipeline/terminologist.go:719`=`res.Consolidated++` | ⛔ НЕ вливать базисные в общий набор до цикла счёта: поле сменит смысл без единой строки об этом | +| счётчики `Consolidated`/`Declined`/`Unanswered` | `backend/internal/pipeline/terminologist.go:722`=`res.Consolidated++` | ⛔ НЕ вливать базисные в общий набор до цикла счёта: поле сменит смысл без единой строки об этом | | таблица стопа и её сайдкар | ⚠ ИСТОРИЧЕСКОЕ (до этого пака формула была `row.Invented = dst != "" && !proposedByDrafts(dst, c.Variants)`, и адрес намеренно НЕ в якорной форме: цель не уехала, а ИЗМЕНИЛАСЬ — её починил этот пак, и подставить найденный в цели токен значило бы подогнать якорь под то, что там оказалось). Живая строка — `backend/internal/pipeline/mining.go:525`=`row.Invented = dst != "" && !row.SettledByBasis` | отдельное состояние, см. ниже | | очередь спорных | `backend/internal/pipeline/stopcontest.go:83`=`if r.SettledByBank` | ⛔ решается ОТДЕЛЬНО, не наследуется по совпадению имени | | проекция банка наружу | `backend/internal/pipeline/bankexport.go:128`=`NeverAsked: t.BankSettled` и `backend/internal/pipeline/bankexport.go:269`=`Contradicts: row.Contradicts, BankHolds: row.BankHolds` | АДДИТИВНОЕ поле, см. §5 | @@ -1797,7 +1797,7 @@ langpacks — **0 файлов** при **16** прочитанных прибо ##### 6. Юниты (§4.5) — ОТДЕЛЬНЫЙ ПРОХОД, батчер не трогаю Семьи и серии сегодня детектируются НАД `paid`, то есть ПОСЛЕ фильтра -(`backend/internal/pipeline/terminologist.go:574`=`terminology.DetectFamilies(paid, fp)`), и комментарий выше объясняет причину: детектировать над полным +(`backend/internal/pipeline/terminologist.go:577`=`terminology.DetectFamilies(paid, fp)`), и комментарий выше объясняет причину: детектировать над полным набором значит рассогласовать юниты с батчами, из них построенными. **Но это довод про БАТЧИ, а не про предикат.** Если считать юнит над `paid`, юнит будет определён тем самым фильтром, который он питает, — круг. ⇒ юнит для предиката считается **отдельным чистым проходом над ВСЕМИ кандидатами**, батчер остаётся @@ -1828,7 +1828,7 @@ langpacks — **0 файлов** при **16** прочитанных прибо - **Ряд 490** (страж разрыва окон глав) — $0, беру, образец `backend/internal/membank/memvoice.go:271`=`func VoiceWindowGaps`; у образца беру и ГАРАНТИЮ: намеренный разрыв законен ⇒ исход громкая строка, а не отказ загрузки. - **Ряд 448** — пере-формулировка обязана обновить И голден каталога операторских сообщений - (`backend/internal/pipeline/testdata/operator-messages.txt:141`=`terminology: this book had already paid`), и это + (`backend/internal/pipeline/testdata/operator-messages.txt:143`=`terminology: this book had already paid`), и это обслуживание заказанной смены поведения (`D39.183`), а не подгонка под зелень. Объявляю заранее. - **Ряд 487** пак НЕ чинит, но посылку «решено ⇒ `draft`» пиню: без пина завтрашняя починка скачком двинет набор решённых, то есть деньги. @@ -2411,7 +2411,7 @@ decision»). | пункт | исход | чем предъявлено | |---|---|---| -| §4.1 громкий отказ, когда у ЦЕЛИ нет строк инъекции | сделано, и ШИРЕ заказа: арм пять, а не три | `backend/internal/pipeline/injectionpreflight.go:96`=`func (r *Runner) checkInjectionTexts` + вызов `backend/internal/pipeline/runner.go:389`=`if err := r.checkInjectionTexts(logger); err != nil {` рядом с соседним отказом `:364`=`if forWrite && r.Pipeline.Gates.Terminology.Enabled && r.pack == nil {`; пин `TestABankItsTargetCannotStateRefusesBeforeItIsBought` (6 подтестов) | +| §4.1 громкий отказ, когда у ЦЕЛИ нет строк инъекции | сделано, и ШИРЕ заказа: арм пять, а не три | `backend/internal/pipeline/injectionpreflight.go:96`=`func (r *Runner) checkInjectionTexts` + вызов `backend/internal/pipeline/runner.go:401`=`if err := r.checkInjectionTexts(logger); err != nil {` рядом с соседним отказом `:364`=`if forWrite && r.Pipeline.Gates.Terminology.Enabled && r.pack == nil {`; пин `TestABankItsTargetCannotStateRefusesBeforeItIsBought` (6 подтестов) | | §4.1 предикат ПОЛНОТЫ, а не заголовка | сделано | `lang.InjectionTexts.MissingKeys` / `Authored` / `GenderKeyGaps` + реестр `roleInjectionKeys` (`chunkrun.go`); пины `TestTheInjectionKeysAreOneVocabulary`, `TestAPartialInjectionSetIsVisibleToTheGuard` | | §4.2 отказ роли перестаёт быть передачей (путь **а**) | сделано; сентинел НЕ тронут | `terminology.go` — ветвь `declined` рядом с `isNoDst`; пин `TestAProseDeclineLandsInTheDeclineBucketAndNotInTheBank` | | §4.2 мусор разведён с отказом по корзинам | сделано | ветвь `hasLetter` + счётчики `NoLetters`/`DeclinedByPhrase`; пины `TestARenderingWithNoLettersReadsAsUnansweredNotAsCanon` (парсер) и `TestADeclineAndGarbageLandInDifferentBuckets` (сквозной, все три исхода в одном ответе) | @@ -2431,7 +2431,7 @@ decision»). 2. `gates.banknote` — черновик спрашивается о таблице терминов; 3. `book.yaml glossary_seed` — сид; 4. **`.mined-delta.yaml`** — подписанные владельцем строки. ⚠ Путь ДЕРИВИРУЕТСЯ (`backend/internal/config/book.go:210`=`b.MinedDelta = filepath.Join(dir, b.BookID+MinedDeltaSuffix)`), поэтому `book.MinedDelta != ""` истинно ВСЕГДА и как арм негоден — проверяется НАЛИЧИЕ файла (`decisionFilePresent`); -5. **авто-банк, оставленный в сторе предыдущим прогоном** (`backend/internal/pipeline/mining.go:723`=`func (r *Runner) loadAutoBank`). Гейт — ран-локальный конфиг книги (`D39.126` п.2а), а строки в сторе — нет: книга с выключенными гейтами, без сида и с банком от прошлого прогона инжектит так же молча. +5. **авто-банк, оставленный в сторе предыдущим прогоном** (`backend/internal/pipeline/mining.go:754`=`func (r *Runner) loadAutoBank`). Гейт — ран-локальный конфиг книги (`D39.126` п.2а), а строки в сторе — нет: книга с выключенными гейтами, без сида и с банком от прошлого прогона инжектит так же молча. Армы спрашиваются в порядке цены (`backend/internal/pipeline/injectionpreflight.go:40`=`func (r *Runner) bankRowsReachTheWire`): два поля конфига → один `stat` → чтение стора, и стор трогается только когда всё дешёвое сказало «нет». **Ошибка чтения дверью не считается** — WARN и «строк нет», по норме «предупреждение о деньгах не выдумывается из ошибки». diff --git a/docs/architecture/03-implementation-notes.md b/docs/architecture/03-implementation-notes.md index 666bf6cb..eab2a581 100644 --- a/docs/architecture/03-implementation-notes.md +++ b/docs/architecture/03-implementation-notes.md @@ -89,7 +89,7 @@ excision_suspect / ok) 1:1, не изобретать заново. Метрик `configs/models.yaml` (модели/цены/таймауты, с датой проверки). В конфиг ядра входят: состав/порядок стадий, роль→модель, версии промптов, пороги гейтов, **режим инъекции глоссария (selective | full_prefix)** (Р5 требует обе схемы), токен-бюджеты сборки контекста (инъекция, STM, overlap), лимит регенераций до эскалации, -**именованные эскалационные цепочки** (список моделей; ⚠ **испр. 10.09:** какую цепочку берёт прогон, решает ЗАГРУЗКА по `content_policy`, а не раннер — `backend/internal/config/pipeline.go:529`=`LOAD, not by the runner` (⚠ испр. 17.09: было :502); раннер лишь исполняет уже разрешённый хоп, `backend/internal/pipeline/escalation.go:125`=`st.ResolvedHop`. Слово «channel» отставлено — `backend/internal/config/pipeline.go:266`=`LegacyChannel is the RETIRED`), fan-out N для +**именованные эскалационные цепочки** (список моделей; ⚠ **испр. 10.09:** какую цепочку берёт прогон, решает ЗАГРУЗКА по `content_policy`, а не раннер — `backend/internal/config/pipeline.go:597`=`LOAD, not by the runner` (⚠ испр. 17.09: было :502); раннер лишь исполняет уже разрешённый хоп, `backend/internal/pipeline/escalation.go:125`=`st.ResolvedHop`. Слово «channel» отставлено — `backend/internal/config/pipeline.go:332`=`LegacyChannel is the RETIRED`), fan-out N для C2, cache TTL per-стадия. В models.yaml: цены (+cache write/read), профиль таймаутов/ретраев per-модель, `extra_body`. Зашито в раннер: циклы по главам/чанкам, ветвление по гейтам, механика эскалации/ретраев, семантика «эскалация → re-gate → флаг» (диаграмма pipeline.puml перегейтовку не рисует — реализую re-gate @@ -103,7 +103,7 @@ C2, cache TTL per-стадия. В models.yaml: цены (+cache write/read), п ## 5. [НУЖНО РЕШЕНИЕ] — СНЯТО, всё развязано -> ⚠ **§5 СНЯТ — а код на него ещё ссылается, поэтому вот куда он уехал** (заглушка, испр. 10.09, по образцу заглушки §2 выше). Три не-тестовых сайта продолжают адресовать эту секцию: `backend/internal/config/pipeline.go:498`=`a defect flips the chunk to flagged` (⚠ испр. 17.09: было :471) (формула премиум-бюджета — см. `01-decisions.md`, там же сказано, что в коде её НЕТ и почему), `backend/internal/llm/llm.go:106`=`JSONOnly bool` (провайдерский стриминг — отнесён к Ф2, §3 этого дока), `backend/internal/pipeline/render.go:34`=`explicit re-translation, not a silent cache miss` (§3.4 того же дока). ⇒ читающий из кода приходит сюда и находит указатель, а не пустоту. +> ⚠ **§5 СНЯТ — а код на него ещё ссылается, поэтому вот куда он уехал** (заглушка, испр. 10.09, по образцу заглушки §2 выше). Три не-тестовых сайта продолжают адресовать эту секцию: `backend/internal/config/pipeline.go:566`=`a defect flips the chunk to flagged` (⚠ испр. 17.09: было :471) (формула премиум-бюджета — см. `01-decisions.md`, там же сказано, что в коде её НЕТ и почему), `backend/internal/llm/llm.go:106`=`JSONOnly bool` (провайдерский стриминг — отнесён к Ф2, §3 этого дока), `backend/internal/pipeline/render.go:34`=`explicit re-translation, not a silent cache miss` (§3.4 того же дока). ⇒ читающий из кода приходит сюда и находит указатель, а не пустоту. Пять вопросов 04.07 закрыты, карта развязок — в баннере вверху (тела D11/D14/D19/D23 — в слайсе `../archive/architecture/05-decisions-D1-D38.md`); пятый, не попавший в карту, — пункты оркестратору (фазовые пометки диаграмм, владелец задачи cache-hit), отработан арх-ресетом D39. Формулировки вопросов — `../archive/PROGRESS-2026-07-04-10.md`. diff --git a/docs/architecture/05-decisions-log.md b/docs/architecture/05-decisions-log.md index be9ca550..991e5d9b 100644 --- a/docs/architecture/05-decisions-log.md +++ b/docs/architecture/05-decisions-log.md @@ -38,12 +38,12 @@ > ⚠ **Эррата 27.08-д (D39.159 п.7) — норма §3 п.8 применена к СОБСТВЕННОМУ бэклогу, три строки переsуждены.** Свип грепом по путям лендинга `d1eb8a9` нашёл, что лекарство трёх строк уже в дереве. **212 ЗАКРЫТА и снята с таблицы:** голый `yaml.Unmarshal` заменён строгим `seed.DecodeFile` (`backend/internal/membank/memseed.go:51`=`whether the document it is ABOUT to write would load`, код называет строку по номеру), пин двойной — `backend/internal/seed/decode_test.go:16`=`_, err := DecodeFile(` и `backend/internal/membank/seedlint_test.go:39`=`misspelled field name`. **218 ЗАКРЫТА и снята:** три тест-хелпера снесены, `miner_parity_test.go:35` говорит «the old $HOME/books address is dead». **199 СУЖЕНА:** движковая половина цепи доставки правок банка ПОСТРОЕНА, открыт только платформенный конец (пункт 2в очереди) — прежняя формулировка «канал НЕ построен» отправляла бы следующий пак строить построенное. Бэклог 177 → 175. ⚠ Норма окупилась на своём авторе в тот же день, что и на чужом. > ⚠ **Эррата 27.08-е (D39.159 п.8, финал): `PD-398` ЗАКРЫТА — у построенного гейта появился пин.** `selftest_tail_vocab()` гоняется на каждом `--check`, четыре утверждения, проверен ПОСАДКОЙ трёх мутаций самого гейта (все три пойманы, базовая линия молчит). ⚠ Первая редакция пина молчала на одной из трёх: утверждение про вес проверяло `cells()`, а мутация меняет то, чем пользуется `register()`. Пин, проверенный одним прогоном вместо посадки, — ровно тот класс, который эта строка описывает; поймано только потому, что посадку сделал. Регистр 96 → 95 открытых. > ⚠ **Эррата 27.08-ж (D39.160 п.2) — ошибка ОРКЕСТРАТОРА, найденная исполнителем пака.** Нота утверждала, что после сноса отменённой двери «канон и деплой СОВПАДУТ точно», а промт минора (§3.2-бис) — что счётчики `pending_decisions`/`complete` «навсегда нули». **Верно по ПУТЯМ, неверно по ПОЛЯМ:** проекция `GET /bank` продолжает их слать, и это не нули — `bankCountsTx` (`platform/internal/pgstore/readmodel.go:330`) считает `proposed`-строки, о чём говорит её собственный комментарий. Клиент 0.5.0 лишние поля игнорирует, но аллоулист-норма нарушена до монтажа (2в). Носитель — `PD-399`. ⚠ Контрактная сессия принесла это ПИНГОМ по §11 промта, вместо того чтобы тихо подогнать работу под неверную посылку; это и есть поведение, которого норма требует. -> ⚠ **Эррата 27.08-з (D39.156, состав пункта 2в): «воркер решений → глагол перед возобновлением» СНЯТ — посылка изменилась.** Пункт писался, когда двери в контракте не было и подразумевалось НАКОПЛЕНИЕ: платформа копит решения у себя и скармливает их движку перед `resume`. С дверью канона 0.5.0 накопления не существует — правка ПРИМЕНЯЕТСЯ в момент подачи, и гарантия «до возобновления» у синхронной формы СИЛЬНЕЕ воркерной: применено прежде, чем клиент получил `200`. Проверено исполнением с обеих сторон: движок на стопе ВЫХОДИТ (`backend/cmd/tmctl/main.go:95`=`obs.PanicError, row 176`, код 3), флок не-блокирующий и отпускается ядром на выходе процесса (`backend/internal/store/store.go:187`=`acquireLock takes a non-blocking exclusive flock`), между стопом и возобновлением живого процесса на проекте нет — запинено `pipeline/bankchain_test.go:62-64`=`The door takes the project`; кап 5000 решений выведен ИМЕННО из синхронности («the call stops fitting the caller's timeout»). Остаётся не воркер, а пер-книжная сериализация в обработчике. `Resume` «с решениями как они есть» не тронут. +> ⚠ **Эррата 27.08-з (D39.156, состав пункта 2в): «воркер решений → глагол перед возобновлением» СНЯТ — посылка изменилась.** Пункт писался, когда двери в контракте не было и подразумевалось НАКОПЛЕНИЕ: платформа копит решения у себя и скармливает их движку перед `resume`. С дверью канона 0.5.0 накопления не существует — правка ПРИМЕНЯЕТСЯ в момент подачи, и гарантия «до возобновления» у синхронной формы СИЛЬНЕЕ воркерной: применено прежде, чем клиент получил `200`. Проверено исполнением с обеих сторон: движок на стопе ВЫХОДИТ (`backend/cmd/tmctl/main.go:95`=`obs.PanicError, row 176`, код 3), флок не-блокирующий и отпускается ядром на выходе процесса (`backend/internal/store/store.go:187`=`acquireLock takes a non-blocking exclusive flock`), между стопом и возобновлением живого процесса на проекте нет — запинено `backend/internal/pipeline/bankchain_test.go:62-64`=`The door takes the project`; кап 5000 решений выведен ИМЕННО из синхронности («the call stops fitting the caller's timeout»). Остаётся не воркер, а пер-книжная сериализация в обработчике. `Resume` «с решениями как они есть» не тронут. > ⚠ **Эррата 28.08-и (D39.165 §3, размер мины) — ошибка ОРКЕСТРАТОРА, найденная опровергателем промта P10.** Нота утверждает: «первый же ПРОДОЛЖАЮЩИЙ прогон после первой же правки банка УПАДЁТ». **Переоценено.** Гард снапшота стреляет по СУЩЕСТВУЮЩЕМУ джобу (`backend/internal/pipeline/stagerun.go:48`=`r.Store.EnsureJob` ⚠ (адрес испр. 10.09: строку сдвинул лендинг `3f05fab`) — `EnsureJob` создаёт джоб стадии в момент, когда стадия впервые исполняется), а правка в ГЛАВНОМ окне — стоп подписи `awaiting_bank` — двигает edit-снапшот, когда edit-джобов ЕЩЁ НЕТ: возобновление создаёт их свежими, и гард молчит. Драфт-волна mined-строк не видит вовсе (`backend/internal/pipeline/bankmaterialize.go:321`=`if row.Source == "mined"` (⚠ адрес испр. 06.09; ФОРМУЛИРОВКА испр. по эррате 06.09-ж: переименования НЕ было — `seeding.go` жив, а код ВЫДЕЛЕН в `bankmaterialize.go` (`6ec9f8a` 29.08). Линт не мог угадать адрес по другой и более простой причине: прежний якорь был написан ГОЛЫМ именем, а такое имя отсеивается на `docs/scripts/counts.py:618`=`"~" in rel or "/" not in rel` ДО всякой проверки файла — нашёл `git log -S` по тексту якоря)). **Дефект СТОИТ, но его триггер уже: правка, сделанная ПОСЛЕ появления edit-джобов** — пауза потолком посреди редактуры и ДОЧИТАННАЯ книга. ⚠ Срочность при этом НЕ падает: флагманский случай продукта («поправил имя героя в дочитанной книге») — ровно тот, где edit-джобы существуют, то есть мина бьёт именно по нему. **Цена ошибки была бы прямой:** репро на потоке `awaiting_bank` показало бы ЗЕЛЕНЬ без фикса, и пак мог быть отозван как мнимый. Промт P10 §4.2 исправлен: репро обязано фиксировать состояние «edit-джоб существует ДО правки». > ⚠ **Эррата 28.08-к (D39.165 §3 + решение оркестратора о глава-полосе) — ДВЕ ошибки, обе найдены широким самопроходом платформенной сессии, обе доказаны исполнением.** **(1) Посылка «смета УЖЕ публикуется в `status --json`» верна только ПОСЛЕ свёртки.** `bank-apply` пишет только ФАЙЛЫ решений, а `status` считает ре-билл от СОХРАНЁННОГО глоссария (`backend/internal/pipeline/status.go:1011`=`seed-FILE edit` ⚠ (адрес испр. 06.09 и 10.09 — второй раз строку сдвинул лендинг `3f05fab`: цитата УЕХАЛА, не исчезла; в теле `D39.165` она осталась по прежнему адресу — тело ноты не переписывается, D23.3)``, `projectStoredMemory` — его собственный комментарий: «A seed-FILE edit not yet re-run is NOT reflected here… that drift surfaces on the next translate's re-seed»). Свёртка происходит внутри СЛЕДУЮЩЕГО `translate`, поэтому сразу после правки движок отвечает `units=0`/`drift=false`. Следствие: продажа «затронуто N юнитов» и холд от сметы В ТЕКУЩЕМ ШВЕ НЕДОСТИЖИМЫ — для них нужен движковый глагол «свернуть банк и оценить ВНЕ translate», которого нет. **(2) Решение оркестратора «полоса пере-прохода — в ГЛАВАХ» ОТМЕНЯЕТСЯ: его посылка опровергнута.** Я рассудил, что $0-репин двигает полосу, потому что идёт через тот же `resumeFromChunkStatus`, — и не проверил анонс. Движок анонсирует юнит ОДИН РАЗ на жизнь книги (announce-once, `backend/internal/pipeline/events.go:57`=`announce-once keys`), пере-проход не ре-анонсирует ни репины, ни пере-переводы ⇒ `done` остался бы НУЛЁМ навсегда. Это ровно тот класс, от которого предостерегает памятка «не выводить из соседнего механизма, не проверив свой». ⚠ **Что при этом НЕ отменяется:** запрет класть ЮНИТЫ в поле, объявленное в главах, стоит — но объявленная в каноне «одна единица работы» запретом не является, потому что она НЕ молчаливая. > ⚠ **Эррата 29.08-а (D39.172, две строки, объявленные заведёнными) — ошибка ОРКЕСТРАТОРА №19.** Тело ноты дважды утверждает «Заведено строкой» / «строка заведена» — про `Touch`, выбрасывающий `RowsAffected`, и про пересборку `tmctl` в рецепте стенда. **На момент ратификации ни одной из этих строк не существовало:** последняя строка регистра платформы была `PD-430`, и проверка грепом по `Touch|RowsAffected|пересбор` давала только совпадения слов в чужих строках. Утверждение о будущем записано как о свершившемся — ровно тот класс, который эта же смена ловила у сессий трижды. **СНЯТА 29.08: строки заведены зоной — `PD-431` (`Touch`) и `PD-432` (пересборка `tmctl`), `PD-423` получил вторую точку. Проверено грепом по регистру.** Тело ноты не переписывается (D23.3). ⚠ Сюда же третий пункт того же абзаца: `PD-423` предписано ПЕРЕ-ПРОВЕРИТЬ (у сессии `sqlc` тест зелёный в трёх прогонах), и пометки в строке регистра тоже нет. > ⚠ **Эррата 30.08-б (D39.165 §2 и §3, две находки платформенной сессии P12).** (а) **Якорь протух:** §2 цитирует фразу канона о `chapters_done` по `openapi.yaml:1542-1545` — текст уехал на **`1554-1559`**, по прежним строкам сейчас `RejectReason`. Сама цитата верна дословно; нота не битая, битым стал только номер (класс строки бэклога 219). (б) **§3 назвал живое САМОПРОТИВОРЕЧИЕ канона и не дал ему носителя:** правка банка «takes effect on the NEXT run» (`openapi.yaml:504`) против «finished work is not bought twice» плюс подъём потолка только у ПРИОСТАНОВЛЕННОЙ книги (`:590`) — у дочитанной книги следующего прогона купить нечем, поэтому принятая правка умирает молча. Ни в едином бэклоге, ни в регистре платформы строки не было (проверено грепом по обеим фразам) — **заведена строка бэклога 241** 30.08. ⚠ Класс — «названо в теле ноты и не получило карриера»; норма приёмки требует строку ТЕМ ЖЕ лендингом, здесь она не легла два дня. -> ⚠ **Эррата 30.08-а (D39.176 п.5, `PD-421`) — ошибка ОРКЕСТРАТОРА №20, пойманная ВЛАДЕЛЬЦЕМ.** Пункт вынесен владельцу с формулировкой «открытая вкладка теряет сессию примерно через час» — **неверно, и ровно тем классом, от которого предостерегает записка передачи: вывод из соседнего числа вместо проверки.** Час — это ПЕРИОДИЧНОСТЬ `SweepSessions`, а срок жизни сессии другой: `SessionIdleTTL` = **14 суток**, `SessionMaxAge` = **30** (`platform/internal/config/config.go:345-346`=`defaultSessionIdle`), причём окно бездействия скользит на любом аутентифицированном запросе (`platform/internal/auth/middleware.go:66`=`a.Sessions.Touch(r.Context(), digest`; поток `Touch` не зовёт — грепом 0, и это не важно при окне в две недели). Реальное следствие `PD-421` — то, что и записано в её теле: сессия, протухшая по бездействию И подметённая, гасит поток с опозданием до часа, то есть после ДВУХ НЕДЕЛЬ без единого действия. **Вопрос СНЯТ с листа владельца как несущественный; правка в паке P12 не нужна, строка остаётся `info`.** Владелец вопросом «ты уверен или придумываешь?» поймал ошибку до того, как она стала заказом на работу. +> ⚠ **Эррата 30.08-а (D39.176 п.5, `PD-421`) — ошибка ОРКЕСТРАТОРА №20, пойманная ВЛАДЕЛЬЦЕМ.** Пункт вынесен владельцу с формулировкой «открытая вкладка теряет сессию примерно через час» — **неверно, и ровно тем классом, от которого предостерегает записка передачи: вывод из соседнего числа вместо проверки.** Час — это ПЕРИОДИЧНОСТЬ `SweepSessions`, а срок жизни сессии другой: `SessionIdleTTL` = **14 суток**, `SessionMaxAge` = **30** (`platform/internal/config/config.go:456`=`defaultSessionIdle = 14 * 24 * time.Hour`), причём окно бездействия скользит на любом аутентифицированном запросе (`platform/internal/auth/middleware.go:66`=`a.Sessions.Touch(r.Context(), digest`; поток `Touch` не зовёт — грепом 0, и это не важно при окне в две недели). Реальное следствие `PD-421` — то, что и записано в её теле: сессия, протухшая по бездействию И подметённая, гасит поток с опозданием до часа, то есть после ДВУХ НЕДЕЛЬ без единого действия. **Вопрос СНЯТ с листа владельца как несущественный; правка в паке P12 не нужна, строка остаётся `info`.** Владелец вопросом «ты уверен или придумываешь?» поймал ошибку до того, как она стала заказом на работу. > ⚠ **Эррата 29.08-б (D39.171 механизм cgroup + D39.172 число операторов) — ДВЕ ошибки ОРКЕСТРАТОРА №19.** > (а) **Механизм, который я объявил причиной неприменения `MemoryMax`, НЕВЕРЕН.** Я вывел «вызывающий > процесс обязан жить внутри `user@.service`» из пробы `systemd-run --user --scope` без @@ -65,7 +65,7 @@ > ⚠⚠ **Эррата 06.09-з (`D39.213` п.2 и п.3) — ТАБЛИЦА ТРЁХ ПРОГОНОВ ЧИТАЕТСЯ КАК КОНТРОЛИРУЕМОЕ СРАВНЕНИЕ, А БЫЛА ГОЛОСОВАНИЕМ 2:1 ПО ТРЁМ РАЗНЫМ КОНФИГУРАЦИЯМ — и это же описание ПРЯТАЛО мою сильнейшую улику. Нашёл верификатор; пере-проверено мной.** **(а) «Три прогона по ОДНОМУ дереву» — неверно.** Прогон (б) шёл по дереву с уже наложенной починкой, а она несла новый гейт-тест: в популяции `func Test` дерево ушло 849 → 850. **(б) Прогон (в) — ДО починки, а не после.** Мой чистый `go test` написан в **07:36:45**, эррата с его числами опубликована `be45500` в **07:38:50**, а починка `201363b` — в **08:04:37**. ⇒ группировать (б) и (в) как согласных против (а) значило выдавать за контроль то, что им не было. ⭐ **(в) ГЛАВНОЕ: контроль в таблице ЕСТЬ, и он другой.** Разложенные по признакам, а не по порядку: **(а)** до починки · лог ОБЩИЙ, зона гнала параллельно · **21/7**; **(в)** до починки · СВОЙ файл вне каталога зоны · **20/9**; (б) после починки · попрогонный · 20/9. **(а) и (в) — ОБА до починки и различаются расположением лога — и они расходятся.** ⇒ изолированная переменная — РАСПОЛОЖЕНИЕ ЛОГА, а не починка, и (б) не добавляет ничего сверх того, что (в) показал БЕЗ неё. **(г) Механизм предъявлен исполнением, а не правдоподобием** (проба верификатора, повторена мной в своей форме): два писателя, оба через `>` в один файл — как в старом рецепте, — дают **646 строк из 1200**, ОБЕ фракции в файле, 554 строки потеряны. То есть чужие строки появляются, а свои пропадают, и потеря асимметрична: ровно призрачный лишний пакет и спрятанные скипы. ⇒ **статус причины: НЕ догадка и НЕ замер — ВЫВОД, стоящий на до-починочном контроле и воспроизводимом механизме.** Тот конкретный случай невосстановим: лог стёрт самим прибором. ⚠ Урок формы: **один баннер накрыл две эпистемологии, и слабейшая из них была та, ради которой пункт написан.** Статус утверждения объявляется рядом с утверждением, а не общей шапкой на абзац. > ⚠⚠ **Эррата 06.09-ж (эррата 06.09-г · `D39.212` п.6 · эррата 28.08-и) — ТРИ НЕПРАВДЫ, ВЪЕХАВШИЕ В КАНОН ТЕМ ЖЕ ДВИЖЕНИЕМ, КОТОРЫМ Я ИСПРАВЛЯЛ ПРЕДЫДУЩИЕ. Все три — в текстах ПРО СОБСТВЕННЫЕ ОШИБКИ; нашёл Fable 5 сверкой с деревом.** **(а) «Расщепление стояло ЧЕТВЕРО СУТОК» (эррата 06.09-г) — неверно: четыре с половиной ЧАСА.** `3e40c36` — 06.09 **03:00**, `1cae0ac` — 06.09 **07:30**. Полная история границы (`git log -L`, замер Fable, пере-проверен мной по датам коммитов): `f0d8fed` 08.08 02:26 ввёл `minimum: 1` ещё БЕЗ прозы о нуле · `278ffd8` 06.09 01:40 поставил `0` · `3e40c36` 03:00 вернул `1` · `1cae0ac` 07:30 вернул `0`. **Ни один отрезок не равен четырём суткам.** ⚠ Суть эрраты 06.09-г от этого не меняется — расщепление было, и написано оно одним заходом, — но ДЛИТЕЛЬНОСТЬ я не мерил, а написал по ощущению, и ощущение соврало в двадцать раз. **(б) `D39.212` п.6: «семь флагов, ШЕСТЬ ложных, настоящих два» — 6+2≠7; ложных ПЯТЬ.** Зонный журнал называет их поимённо (`target` · `RunRequest.chapters` · `.characters` · `character_count_exact` · `kind`), и пять плюс два дают семь. Число «шесть» я взял из ПИСЬМА зоны, не пересчитав, — при том что её собственный журнал держит верное. **Ровно класс эрраты 06.09-д: величина, перенесённая из чужого текста без пере-счёта, — и во второй раз за смену подряд.** ⚠ И арифметика была видна на месте: сумма не сходилась с итогом в той же фразе. **(в) «Код переехал вместе с ПЕРЕИМЕНОВАНИЕМ файла» (аннотация «испр. 06.09» внутри эрраты 28.08-и) — переименования не было.** `backend/internal/pipeline/seeding.go` ЖИВ и сегодня (128 строк); `bankmaterialize.go` **ДОБАВЛЕН** `6ec9f8a` 29.08, и в него код ВЫДЕЛЕН, а не переименован вместе с файлом. Верная причина, по которой линт не мог угадать адрес, — другая и проще: якорь был написан ГОЛЫМ именем, а голое имя отсеивается на `docs/scripts/counts.py:618`=`"~" in rel or "/" not in rel` ДО всякой проверки файла. Та же ложь стояла в строке 306 бэклога и там исправлена. ⚠ **Урок общий для всех трёх: текст про собственную ошибку пишется в тот момент, когда автор уверен, что уж ЭТО он знает, — и потому проверяется хуже обычного.** Ни одну из трёх не поймал бы ни один гейт: все три проза, и все три проверяемы одной командой, которую я не набрал. > ⚠ **Эррата 06.09-е (D39.211 п.1) — ТОТ ЖЕ НЕДОСЧЁТ ПАКЕТОВ УНАСЛЕДОВАН РАТИФИЦИРОВАННЫМ АКТОМ, А ТОЧНЫЙ РАЗБОР УЖЕ НЕВОССТАНОВИМ.** Пункт 1 цитирует числа зоны v13: «`MAKE-EXIT=2`, **21 пакет**, 20 ok». Пакетов у платформы **ДВАДЦАТЬ** — пере-считано 06.09 чистым `go test ./... -race -count=1 -v`: 20 различных, ни одного без тестов; список зона предъявила поимённо. ⇒ «21» здесь — то же, что в эррате 06.09-д: число СТРОК вывода, а не пакетов. ⛔ **А вот раскладка «20 ok» при `FAIL 1` — ⛔ **испр. 06.09 по находке верификатора: она НЕ БЫЛА УТРАЧЕНА, и ОБЕ причины, которыми я это объяснил, ложны.** **(а)** Раскладка лежала в зонном журнале — «ПАКЕТОВ 21: ok 20, FAIL 1» и «ТЕСТОВ верхнеуровневых 850: PASS 844 · FAIL 1 · SKIP 5» — **в ТОМ ЖЕ коммите `3327ac8`, которым эта эррата объявила её невосстановимой**: обвинение и улика заландены одним моим движением. **(б)** Прибор её не стирал: v13 был КРАСНЫМ (`MAKE-EXIT=2`), а рецепт до починки на красном пути выходит ДО `rm -f` и вслух обещает лог сохранить (`git show 201363b^:platform/Makefile`: `status -ne 0` ⇒ «the log is kept at .check.log» ⇒ `exit 1`, а `rm -f` достижим только на зелёном). Лог затёрт СЛЕДУЮЩИМ прогоном по фиксированному имени — другой механизм, и как раз тот, что чинился. ⇒ **снят и ⭐-вывод «цена дефекта строки 310, предъявленная не рассуждением, а СЛУЧАЕМ»: случая не было.** Сам дефект строки 310 реален, но иллюстрацию к нему я выбрал неверно дважды. ⚠⚠ **И худшее здесь не ошибка, а её форма: моя «реконструкция» (19 прошедших + 1 упавший + строка-дубль) угадала ВЕРНО — угадывая там, где лежал ЗАМЕР.** Пометка «это реконструкция, а не замер» выглядела осторожностью и ею не была: осторожность — это `git show` по собственному коммиту, а не оговорка при тексте. ⚠ Прочие числа пункта 1 под сомнение НЕ ставятся и подтверждены косвенно: «тестов 850» той же строки согласуется с ростом счёта на один после нового пина (`runs.TestABookWithNothingLeftIsRefusedRatherThanSoldAsAZeroChapterRun`) — ⚠ **довод ВЕРЕН, испр. дважды 06.09: я снял его по находке верификатора, а он находку отозвал, и отзыв верен.** В популяции прибора: `32a6050` — **850**, после ровно ОДНОГО пина `33b896b` — **851**. Ровно +1, ровно один пин. Прежнее снятие опиралось на счёт `func Test`, приложенный к числам другой популяции (`D39.202` п.2г). ⚠ И отдельно: «21 пакет из 21» в `D39.170` — НЕ ошибка и не трогается, там речь о батарее ДВИЖКА, у которого свой набор пакетов; проверено прежде, чем править. -> ⚠⚠ **Эррата 06.09-д (D39.212 п.8) — ЧИСЛА ПРИЁМКИ, КОТОРЫЕ Я НАЗВАЛ СВОИМИ, СНЯТЫ ПРИБОРОМ С ДОКАЗАННЫМ ДЕФЕКТОМ, И ДВА ИЗ НИХ НЕВЕРНЫ.** **(а) Пакетов ДВАДЦАТЬ, а не 21.** 21 — это число строк `^ok`, среди которых `internal/money` напечатан ДВАЖДЫ; различных пакетов двадцать. ⭐ **Платформенная зона пришла к тому же числу ДРУГИМ путём** (у неё лишней была голая итоговая строка `FAIL` рядом со строкой упавшего ПАКЕТА) и поймала себя сама. То есть **одна и та же ошибка — величина, снятая с вывода прибора без разбора, что прибор печатает, — поймала обе стороны НЕЗАВИСИМО, в один час, на одном выводе**; это сильнее любого из двух наблюдений по отдельности. **(б) Скипов ДЕВЯТЬ, а не семь, и семь были не ошибкой счёта, а ложной уликой.** Число 7 снято из `make check`, чей лог живёт по фиксированному пути `platform/.check.log` и затирается параллельным прогоном (строка 310, дефект найден зоной в тот же час). ⛔ **Зона меня об этом предупредила, и я возразил «у меня обошлось — в моём логе нет ни одного `No such file`». Возражение было слабым: отсутствие СИМПТОМА не есть доказательство целости улики**, — и чистый пере-прогон это подтвердил. Строка 310 укусила ровно того, кто её завёл, в тот же час, что и завёл. **(в) Числа, снятые `go test ./... -race -count=1 -v` в файл ВНЕ каталога зоны:** `GO-EXIT=0` · **20 пакетов ok, ни одного без тестов** · верхнеуровневых прогонов **851 — и это верно для минуты прогона** (⚠ **испр. дважды 06.09.** Число было снято, а затем ВОЗВРАЩЕНО: верификатор нашёл «851 не существовало ни на одном коммите», я снял его на `44faafc`, после чего он сам же отозвал находку, и отзыв верен. **Пере-мерено мной ТРЕТЬИМ способом, чистыми экспортами `git archive`:** прибор считает верхнеуровневые ПРОГОНЫ вывода `go test -v`, а это `Test` **плюс `Fuzz`** — фаззер в обычном режиме гоняет сид-корпус и печатает `--- PASS: FuzzXxx`; у платформы таких таргетов два, и они стоят в моём же логе (`FuzzDecoder`, `FuzzSafeReturnTo`). Отсюда: `32a6050` 07:20 — 848+2=**850** · `93f098c` 07:33 — 849+2=**851** · `e92d411` 08:06 — 850+2=**852**. Лог написан 07:36, когда стоял `93f098c`, ⇒ **851 принадлежит настоящему коммиту**, и разложение 842 PASS + 9 SKIP сходится с деревом, а не только само с собой. На пине — **852**, разница ровно в гейт-тесте, заланденном `201363b`. ⛔ **Ошибка и находки, и моей проверки — ОДНА: сравнивались РАЗНЫЕ ПОПУЛЯЦИИ** — `func Test` против «верхнеуровневых прогонов», расходящиеся на два `Fuzz`. ⛔⛔ **И моя проверка не была проверкой: я пере-мерил ЕГО методом и потому воспроизвёл его ошибку, а не нашёл её.** Совпадение двух замеров доказывало лишь, что мы считаем одно и то же не то) · с подтестами PASS 1094 · `ALARM PD-count: 12 (baseline 12)`. ⛔ **Клауза «сошлось с зоной с двух сторон» СНЯТА 06.09 по находке верификатора.** Стояло: «у неё на тех же **851** — PASS 846 · SKIP 5, и разница ровно ЧЕТЫРЕ теста». **Зонный носитель этих чисел не держит ни в ту минуту, ни на пине:** `be45500` — «850: PASS 844 · FAIL 1 · SKIP 5», `e92d411` — «852: PASS 847 · FAIL 0 · SKIP 5». Числа пришли ПИСЬМОМ зоны, а я приписал их её ЖУРНАЛУ. **Тот же класс, что эррата 06.09-ж(б)** («шесть» взято из письма без пере-счёта) — в том же наборе эррат, в тот же час, и именно в клаузе, предъявленной как САМАЯ сильная улика набора. ⚠ Моя собственная норма «число называет ДЕРЕВО и ПОПУЛЯЦИЮ» поймала бы его: у этого числа не было ни того, ни другого. Разница между стендами реальна и объяснена гейтами стенда, но «двустороннее схождение» из неё не следует. **(г) Якорь п.6 был наведён на НЕУНИКАЛЬНЫЙ токен** и пере-наведён (`2ed1c0b`): `Structure *string` встречается в `v0.go` ДВАЖДЫ — `:216` (`wireBook`) и `:308` (`wireOrderOptions`), — так что скрипт мог сесть на чужое поле, а линт бы позеленел. Теперь `v0.go:306-308`=`null when no manifest`, токен по файлу единственный. ⇒ **уточнение к п.7 той же ноты: токен надёжнее номера строки только когда он УНИКАЛЕН. Неуникальный токен ХУЖЕ номера — номер врёт громко и ловится линтом, а он врёт молча и линт зеленит.** +> ⚠⚠ **Эррата 06.09-д (D39.212 п.8) — ЧИСЛА ПРИЁМКИ, КОТОРЫЕ Я НАЗВАЛ СВОИМИ, СНЯТЫ ПРИБОРОМ С ДОКАЗАННЫМ ДЕФЕКТОМ, И ДВА ИЗ НИХ НЕВЕРНЫ.** **(а) Пакетов ДВАДЦАТЬ, а не 21.** 21 — это число строк `^ok`, среди которых `internal/money` напечатан ДВАЖДЫ; различных пакетов двадцать. ⭐ **Платформенная зона пришла к тому же числу ДРУГИМ путём** (у неё лишней была голая итоговая строка `FAIL` рядом со строкой упавшего ПАКЕТА) и поймала себя сама. То есть **одна и та же ошибка — величина, снятая с вывода прибора без разбора, что прибор печатает, — поймала обе стороны НЕЗАВИСИМО, в один час, на одном выводе**; это сильнее любого из двух наблюдений по отдельности. **(б) Скипов ДЕВЯТЬ, а не семь, и семь были не ошибкой счёта, а ложной уликой.** Число 7 снято из `make check`, чей лог живёт по фиксированному пути `platform/.check.log` и затирается параллельным прогоном (строка 310, дефект найден зоной в тот же час). ⛔ **Зона меня об этом предупредила, и я возразил «у меня обошлось — в моём логе нет ни одного `No such file`». Возражение было слабым: отсутствие СИМПТОМА не есть доказательство целости улики**, — и чистый пере-прогон это подтвердил. Строка 310 укусила ровно того, кто её завёл, в тот же час, что и завёл. **(в) Числа, снятые `go test ./... -race -count=1 -v` в файл ВНЕ каталога зоны:** `GO-EXIT=0` · **20 пакетов ok, ни одного без тестов** · верхнеуровневых прогонов **851 — и это верно для минуты прогона** (⚠ **испр. дважды 06.09.** Число было снято, а затем ВОЗВРАЩЕНО: верификатор нашёл «851 не существовало ни на одном коммите», я снял его на `44faafc`, после чего он сам же отозвал находку, и отзыв верен. **Пере-мерено мной ТРЕТЬИМ способом, чистыми экспортами `git archive`:** прибор считает верхнеуровневые ПРОГОНЫ вывода `go test -v`, а это `Test` **плюс `Fuzz`** — фаззер в обычном режиме гоняет сид-корпус и печатает `--- PASS: FuzzXxx`; у платформы таких таргетов два, и они стоят в моём же логе (`FuzzDecoder`, `FuzzSafeReturnTo`). Отсюда: `32a6050` 07:20 — 848+2=**850** · `93f098c` 07:33 — 849+2=**851** · `e92d411` 08:06 — 850+2=**852**. Лог написан 07:36, когда стоял `93f098c`, ⇒ **851 принадлежит настоящему коммиту**, и разложение 842 PASS + 9 SKIP сходится с деревом, а не только само с собой. На пине — **852**, разница ровно в гейт-тесте, заланденном `201363b`. ⛔ **Ошибка и находки, и моей проверки — ОДНА: сравнивались РАЗНЫЕ ПОПУЛЯЦИИ** — `func Test` против «верхнеуровневых прогонов», расходящиеся на два `Fuzz`. ⛔⛔ **И моя проверка не была проверкой: я пере-мерил ЕГО методом и потому воспроизвёл его ошибку, а не нашёл её.** Совпадение двух замеров доказывало лишь, что мы считаем одно и то же не то) · с подтестами PASS 1094 · `ALARM PD-count: 12 (baseline 12)`. ⛔ **Клауза «сошлось с зоной с двух сторон» СНЯТА 06.09 по находке верификатора.** Стояло: «у неё на тех же **851** — PASS 846 · SKIP 5, и разница ровно ЧЕТЫРЕ теста». **Зонный носитель этих чисел не держит ни в ту минуту, ни на пине:** `be45500` — «850: PASS 844 · FAIL 1 · SKIP 5», `e92d411` — «852: PASS 847 · FAIL 0 · SKIP 5». Числа пришли ПИСЬМОМ зоны, а я приписал их её ЖУРНАЛУ. **Тот же класс, что эррата 06.09-ж(б)** («шесть» взято из письма без пере-счёта) — в том же наборе эррат, в тот же час, и именно в клаузе, предъявленной как САМАЯ сильная улика набора. ⚠ Моя собственная норма «число называет ДЕРЕВО и ПОПУЛЯЦИЮ» поймала бы его: у этого числа не было ни того, ни другого. Разница между стендами реальна и объяснена гейтами стенда, но «двустороннее схождение» из неё не следует. **(г) Якорь п.6 был наведён на НЕУНИКАЛЬНЫЙ токен** и пере-наведён (`2ed1c0b`): `Structure *string` встречается в `v0.go` ДВАЖДЫ — `:216` (`wireBook`) и `:308` (`wireOrderOptions`), — так что скрипт мог сесть на чужое поле, а линт бы позеленел. Теперь `platform/internal/httpapi/v0.go:336-336`=`null when no manifest`, токен по файлу единственный. ⇒ **уточнение к п.7 той же ноты: токен надёжнее номера строки только когда он УНИКАЛЕН. Неуникальный токен ХУЖЕ номера — номер врёт громко и ловится линтом, а он врёт молча и линт зеленит.** > ⚠⚠ **Эррата 06.09-г (эррата 06.09-в, клаузула (а)) — `minimum: 1` НАПИСАН НЕ ПО НЕЗНАНИЮ: ТЕМ ЖЕ ДВИЖЕНИЕМ Я СНЁС УЖЕ СТОЯВШИЙ ВЕРНЫЙ ОТВЕТ.** Эррата 06.09-в числит его ложью «второго захода» — это мягче факта. Дифф `3e40c36` (канон `0.12.0`, мой): в поле стояло `type: integer` + **`minimum: 0`** и абзац «`0` is legal and is not «nothing was ordered»: a re-pass buys no chapters and reports `0` here … A client that treats this as at-least-one will reject valid answers». **Коммит заменил `minimum: 0` на `minimum: 1` и в ТОМ ЖЕ заходе пере-написал абзац в сегодняшний «⚠ `0` is legal and means a re-pass … A client that treats this field as at-least-one will reject valid answers».** ⇒ знание не потеряно, а **РАСЩЕПЛЕНО: половина документа, которую читает человек, сохранила правду; половина, которую читает генератор клиента, получила ложь** — и они стояли в шести строках друг от друга **четыре с половиной часа** (⚠ испр. эрратой 06.09-ж: прежняя редакция писала «четверо суток» — длительность не измерялась вовсе). ⛔ Класс не «утверждение без прогона», а **регрессия против собственного канона**: неверное утверждение спорит с кодом и рано или поздно ловится, а снос верной гарантии уничтожает уже оплаченное знание — ровно то, о чём предупреждает шапка `CLAUDE.md`. ⚠ И это ЕЩЁ ОДНО свидетельство строки 309: гейт версии сверял числа, а документ противоречил сам себе внутри одного поля. ⭐ **Следствие для дофикса, снимающее с зоны половину работы:** сочинять нечего, верный ответ лежит в `git show 3e40c36^:docs/architecture/14-api-contract/openapi.yaml`; дело зоны — сверить его со своим пином и вернуть границу. ⚠ Соседний `minimum: 1` у `ordered_units` ВЕРЕН и не трогается: заказа на ноль юнитов не существует, а «заказан не юнитами» уже несёт `null`. > ⚠⚠ **Эррата 06.09-в (D39.211 п.2 и п.6) — АКТ НЕДОСЧИТАЛ СОБСТВЕННЫЕ ОШИБКИ АВТОРА, И НЕДОСЧЁТ ТОГО ЖЕ КЛАССА, ЧТО САМИ ОШИБКИ.** **(а) «ТРИ ЛЖИ В КАНОНЕ» — не три, а НЕ МЕНЬШЕ ШЕСТИ за день, и все мои.** Пункт 2 честен по СОСТАВУ и неверен по ЧИСЛУ: он посчитал то, что нашёл ВТОРОЙ КРУГ, и поставил это под подпись «сколько было». Не названы: `run_limit_reached` описан как «доставлено всё» · `minimum: 1` первого захода · члены ВХОДА `RunRequest` — все три мои, все поправлены, перечень предъявлен Fable 5. ⚠ И шесть — нижняя граница: финальным чтением 06.09 он нашёл в каноне ещё две мои лжи, стоявшие в ту минуту ЖИВЫМИ — `minimum: 1` ВТОРЫМ заходом под собственным описанием поля «`0` законен и означает ре-проход», и абзац `Progress`, объявивший «ровно один дискриминатор — `delivered_chapters`», тогда как ре-проход отдаёт `0`, то есть ЧИСЛО, и по моему же правилу читался бы как заказ в главах. ⛔ **Обе написаны ТЕМ ЖЕ актом, который в п.3(а) ратифицирует «утверждение канона о ЗНАЧЕНИИ пишет ЗОНА из своих пинов»** — норма нарушена документом, который её вводит; лечение по ней же: обе правит платформенная зона из пинов, я ратифицирую текст. ⚠ Урок — ровно строка 306: **число, отвечающее на вопрос «сколько НАЙДЕНО», под подписью «сколько ЕСТЬ», есть прибор, отвечающий на свой вопрос** (`D39.202`). **(б) «МОИ ДВЕ ОШИБКИ КРУГА» — их ТРИ.** Третья не записана нигде: **промт отработавшего пака оставлен ЖИВЫМ ЗАДАНИЕМ.** Замер: форма заказа принята `6ace7e1` (00:47), её промт ретирован `c05a353` (01:43) — **56 минут отработанный пак стоял активным заданием** для холодной сессии; движковый пак принят `9d4c59f` (01:43), промт ретирован `898c840` (01:51). Второй раз — ПОВТОР (⛔ **испр. 06.09 по находке верификатора: причинной связки, которая тут стояла, БЫТЬ НЕ МОЖЕТ.** Стояло «он состоялся потому, что первый чинился как ЭКЗЕМПЛЯР». По секундам: пак форматов принят `9d4c59f` в **01:43:21**, а промт формы заказа ретирован `c05a353` в **01:43:59** — то есть второе нарушение НАЧАЛОСЬ на 38 секунд РАНЬШЕ, чем возникла первая починка. ⇒ оба — параллельные следствия ОТСУТСТВИЯ нормы, а не следствие узкой починки. ⚠ Вывод не страдает, неверна была история вокруг него: **правдоподобная причинность, надетая на верные числа**, — тот же класс, что и всё сегодняшнее). Норма нужна ровно потому, что нигде не сказано, что **приёмка пака и ретирование его промта — ОДИН акт**, а холодная сессия по `CLAUDE.md` идёт в таблицу активных промтов ПЕРВЫМ делом и берёт оттуда своё задание. ⇒ норма записана здесь и в ролевой промт. Тот же класс, что эррата 06.09-б: правило, написанное про экземпляр, не защищает от класса. **(в) ⛔ ОТСТУПЛЕНИЕ, А НЕ НОРМА: `f1e9226` — мой коммит в ЧУЖУЮ ЗОНУ.** Три файла `backend/` (`bookfile/model.go`, `chunk/ingest_test.go`, `chunk/ingestnotes_test.go`; правка комментариев) заландены мной 06.09 в 07:03 на том основании, что бэкенд-сессия объявила в канале, что больше ничего не пишет. **Основание негодное:** `CLAUDE.md` говорит «сессия считается ЖИВОЙ, пока ВЛАДЕЛЕЦ не сказал обратное», и собственное заявление сессии этим словом не является; зону правит зона. Записываю ОТСТУПЛЕНИЕМ и НЕ ратифицирую: «гигиена комментариев в зоне без живой сессии — оркестратору» расширяет мой собственный мандат, а это решение владельца. Содержимое верно и цело, историю не переписываю; вопрос владельцу вынесен в отчёт. ⚠ Молчание было бы ровно тем, в чём эррата 06.09-б уличает мой же гард: **отступление, не названное отступлением, назавтра читается нормой.** > ⚠ **Эррата 06.09-б (эррата 05.09-к) — ГАРД, НАПИСАННЫЙ ПРОТИВ ЛОВУШКИ, БЫЛ СУЖЕН ДО ЭКЗЕМПЛЯРА И ЛОВУШКА СРАБОТАЛА ТРЕТИЙ РАЗ.** Эррата 05.09-к назвала верный корень (pathspec берёт файл ЦЕЛИКОМ и от чужого ВНУТРИ файла не защищает) и внесла исполнимый шаг — но шаг назвал **один файл**, `docs/PROGRESS.md`. 06.09 тот же класс сработал на **`platform/docs/platform-PROGRESS.md`**: мой коммит правки одного якоря унёс ЧЕТВЁРТЫЙ ЗАХОД платформенной зоны — её проверку собственных утверждений, сделанную по прямому вопросу владельца. Содержимое цело, потеряна атрибуция; историю не переписывал. ⇒ **Шаг расширен на КЛАСС** (любой зонный журнал: `docs/PROGRESS.md` · `platform-PROGRESS.md` · `frontend-PROGRESS.md` · `DEFECT_REGISTER.md`). ⚠ Урок на уровень выше прежнего: **правило, написанное про ЭКЗЕМПЛЯР, не защищает от КЛАССА** — и это ровно то, в чём смена уличала зоны весь день, только теперь в норме, которую писал я сам. @@ -156,7 +156,7 @@ > ⚠ **Эррата 11.09-ж (`D39.246` п.5, число «0 FAIL» в рецепте стенда) — ЧИСЛО БЫЛО УСЛОВНЫМ, И УСЛОВИЕ Я НЕ НАЗВАЛ.** Моя приёмка вписала в `platform/docs/STACK_DECISIONS.md` «20 ok · 0 FAIL · 5 скипов» как свойство рецепта. Замер платформенной сессии 11.09 (контроль: 15 файлов возвращены к `git show HEAD:`, два новых удалены, тот же FAIL воспроизведён) показывает: **рецепт не может дать зелёный `internal/runner` НИ ПРИ КАКОЙ конфигурации** — $0-шаблон нужен `TestTheSnapshotGuardIsLoudWithoutTheFlagsAndPassesWithThem`, и он же обнуляет проекцию цены, которую читают `TestTheRealEnginesPriceProjectionIsReadByThisBuild` и `TestWhatTheLiveManifestCarriesAndThisBuildDeclinesToRead`. Моё «0 FAIL» согласуется с ПЛАТНЫМ шаблоном плюс выполненным условием скипа. ⛔ **И под этим лежит опасность дороже неверного числа: предикат скипа неверен ПО РОДУ.** `platform/internal/runner/translate_resnapshot_live_test.go:41-43` слушает `127.0.0.1:11434` и скипает, только если порт ЗАНЯТ, — то есть отвечает на вопрос «свободен ли порт», а НЕ «разрешил ли кто-нибудь тратить деньги». Условий скипа три (переменные гейта · порт · артефакт контраста), и **хорошо провизионированный хост выполняет все три** — то есть платная ветвь рецепта сработает именно там, где всё настроено правильно. Прецедент верного рода в дереве есть: `TM_LIVE`. ⇒ раздел «Гейты батареи» остановлен ⛔-баннером зоной 11.09 до починки; носитель — `П-22`. ⭐ **Класс, и он мой: я вписал в чужой носитель число, не назвав условий, при которых оно снято.** Условное число без условия читается как свойство предмета — и следующая смена получит красное и решит, что сломала сама. Нашла зона, не я. > ⚠ **Эррата 11.09-б (АДРЕС ИСПР. 11.09: носители — промт пака и таблица `docs/README.md`, НЕ тело `D39.247`) — «18 РАСХОЖДЕНИЙ» ЗАВЫШЕНО В 2.5 РАЗА, настоящих СЕМЬ.** Замерено бэкенд-сессией пака консистентности 11.09 по-юнитно: из восемнадцати промахов **11 ложные** — банковская форма доехала до читателя в том же юните СКЛОНЁННОЙ, а `SameStem` её не признал, потому что стеммер снимает ровно одно окончание и стеммы выходят разной длины (во всех одиннадцати один — строгий префикс другого с разницей в ОДИН знак). Пять из одиннадцати — намеренно исключённый мягкий знак (`D39.71`), и записка в дереве сама называет лечение — «anchor-gated match». ⇒ **акт остаётся верен в направлении и неверен в величине:** механизм консистентности работает и работает не везде, но НЕ-мест семь, а не восемнадцать. Носитель — строка бэклога **419**. ⚠ Класс: я опубликовал число, снятое ОДНИМ прибором, не спросив, что этот прибор не видит. ⛔ **Второй класс, пойманный ревью 11.09: эррату я адресовал НЕ ТУДА.** Числа «18» в теле `D39.247` нет ни разу — пере-снято грепом по живым докам (контроль: прибор прочёл 101 файл `docs/`, число нашлось в промте пака, в строке таблицы `docs/README.md`, в журнале и в ряду **419**). Акт говорит ДРУГУЮ величину на ДРУГОЙ популяции — «16 из 69, из них 6 отданы не банковской формой», снятую глазами по отгруженному файлу, и поправка стеммера её НЕ трогает. ⇒ поправлять надо было промт и таблицу, а акт — не надо было. ⭐ Урок: **у эрраты есть адрес, и он проверяется грепом ЧИСЛА по телу ноты, а не памятью о том, откуда я его взял.** > ⚠ **Эррата 11.09-в (D39.247 п.4 и вопрос владельцу) — Я ПИСАЛ ПО ОСИ, КОТОРОЙ В ПРОДУКТЕ НЕТ.** Акт называл «подписанный/неподписанный ТЕРМИН» и ставил владельцу вопрос «закон ли для неподписанного». Владелец 11.09: «нет такого понятия, подписан либо ВЕСЬ банк, либо он в неконсистентном состоянии; есть галочка скипать подпись — тогда автоподписывание». Это ратифицировано `D39.144` (подписывается банк ЦЕЛИКОМ; пер-термная подпись — НЕ модель продукта; дефолт — авто-продолжение с неподписанным банком, строки едут с пометкой). ⇒ **вопрос был сформулирован неверно и снят.** ⭐ Но под ним лежит РЕАЛЬНАЯ находка, и она дороже: в коде есть пер-термный автомат `auto|draft|approved`, и пост-проверка считает нарушением только `approved` — **механизм решает по единице, которой в продуктовой модели не существует.** Носители — строки **407** (исправлена) и **419**. Мерить надо состоянием КНИГИ (подписана целиком либо авто-продолжение), а не статусом отдельного терма. -> ⚠ **Эррата 11.09-г (D39.247 п.4, ряд 418) — РАСХОЖДЕНИЯ ДВУХ ЧИСЕЛ ДВИЖКА НЕ СУЩЕСТВУЕТ, это была моя ошибка ЧТЕНИЯ.** Акт завёл ряд на «`spread=4` при трёх формах в списке» и «`other proposals` 2 против `drafts` 3». Пере-снято бэкенд-сессией и пере-проверено мной: движок печатает ДВЕ РАЗНЫЕ величины В ОДНОЙ СТРОКЕ — `spread=%d conventions=%d` (`backend/internal/pipeline/mining.go:571`=`spread=%d conventions=%d`), где `Spread()` = Σ сырых форм, `Conventions()` = len(свёрнутых классов); комментарий у определения говорит прямо: «Spread 3 with Conventions 3 is a real contest, and the two must not read the same at the stop». Распределение по 69 термам прогона: 60×(1,1) · 6×(2,2) · 2×(3,3) · 1×(4,3) — противоречий ноль. Второй «экземпляр» того же класса: `other proposals` печатает `props[1:]`, то есть ИСКЛЮЧАЕТ текущий `dst`, поэтому 3 черновика дают «other proposals: 2». **Ряд 418 удалён из таблицы; работы он не несёт.** ⚠ Класс ошибки — тот, что эта же смена записала нормой: сравнил число с длиной списка, меряющего ДРУГУЮ величину, и обе половины по отдельности были верны. Пин равенства вешает пак консистентности, чтобы объяснение держалось не текстом. +> ⚠ **Эррата 11.09-г (D39.247 п.4, ряд 418) — РАСХОЖДЕНИЯ ДВУХ ЧИСЕЛ ДВИЖКА НЕ СУЩЕСТВУЕТ, это была моя ошибка ЧТЕНИЯ.** Акт завёл ряд на «`spread=4` при трёх формах в списке» и «`other proposals` 2 против `drafts` 3». Пере-снято бэкенд-сессией и пере-проверено мной: движок печатает ДВЕ РАЗНЫЕ величины В ОДНОЙ СТРОКЕ — `spread=%d conventions=%d` (`backend/internal/pipeline/mining.go:599`=`spread=%d conventions=%d`), где `Spread()` = Σ сырых форм, `Conventions()` = len(свёрнутых классов); комментарий у определения говорит прямо: «Spread 3 with Conventions 3 is a real contest, and the two must not read the same at the stop». Распределение по 69 термам прогона: 60×(1,1) · 6×(2,2) · 2×(3,3) · 1×(4,3) — противоречий ноль. Второй «экземпляр» того же класса: `other proposals` печатает `props[1:]`, то есть ИСКЛЮЧАЕТ текущий `dst`, поэтому 3 черновика дают «other proposals: 2». **Ряд 418 удалён из таблицы; работы он не несёт.** ⚠ Класс ошибки — тот, что эта же смена записала нормой: сравнил число с длиной списка, меряющего ДРУГУЮ величину, и обе половины по отдельности были верны. Пин равенства вешает пак консистентности, чтобы объяснение держалось не текстом. > ⚠ **Эррата 03.09-а (D39.170) — формула «покупатель платит за N юнитов и получает ровно N» БОЛЬШЕ НЕ ВЕРНА.** Пак «число согласия на пути второй покупки» (лендинг 03.09) закрыл вторую половину строки 232: юнит, который прежний прогон НАЧАЛ и не отгрузил по оси `finalStageWave`, дописывается ВНЕ гранта. Отсюда грант `--max-units N` оплачивает ДО **2N** выходных юнитов — замерено приёмкой на живом раннере (грант 2 → `Paid()=4`, шесть вызовов провайдера), и тем же замером подтверждено, что в перенос попадает даже юнит, ФЛАГНУВШИЙСЯ без текста, то есть каждая следующая покупка платит за его пере-атаку заново. ⛔ **Семантика НЕ ратифицирована: слово владельца 03.09 — «подумаем на этот счёт», решение отложено.** До него `--max-units` держит не деньги, а слоты; деньги держит только `--ceiling-usd`. Тело D39.170 не переписано (D23.3); живой носитель формулировки — `15-money-path.md`, пункт про `--max-units`. > ⚠ **Эррата 02.09-в — ЧТО РЕАЛЬНО СДЕЛАНО С ТЕЛАМИ 02.09, слово владельца.** Ревизия доков сняла из тел нот **девять** кусков (D39.124 · D39.128 · D39.153 · D39.155) — пересказы и дубли, не решения. Проверено перед решением: у КАЖДОЙ снятой строки отличительный токен живёт ещё в 4–61 файле дерева (`SweepSessions` — 6, `UnsettledRuns` — 15, `RepeatableRead` — 4), уникально не потеряно НИЧЕГО. **Владелец 02.09 велел не откатывать:** заказ был вырезать мусор, а откат вернул бы в журнал ровно то, что вырезали сознательно. ⇒ D23.3 действует по-прежнему для РЕШЕНИЙ и клеймов; сжатие пересказа внутри тела с сохранением решения — не нарушение, а разрешённый случай. Прежняя редакция этой эрраты утверждала, что тела не правились вовсе, и противоречила девяти правкам рядом — снято. @@ -255,11 +255,11 @@ > **(а)** В теле эрраты к `D39.234` п.1б (греп `ЭРРАТА К СОБСТВЕННОМУ АКТУ`): «finish string» живёт на > `backend/internal/pipeline/stagerun.go:420`=`finish string` — в теле стоит `:477`. > **(б)** В теле о расщеплении соседних полей канона (греп `То же расщепление в соседних полях`): «null when no manifest» -> живёт на `platform/internal/httpapi/v0.go:320`=`null when no manifest` +> живёт на `platform/internal/httpapi/v0.go:336`=`null when no manifest` > — в теле стоит `:313-315`. > ⛔ **(в) и (г) — существеннее первых двух, потому что этот адрес ЧИТАЕТ ЧУЖАЯ ЗОНА.** Тело `D39.255` п.10 (греп `ЯКОРНЫЙ ИТОГ ЛЕНДИНГА`) > раздаёт фронту «готовые адреса» по ряду **457** — и оба уехали. Живые: -> `docs/architecture/14-api-contract/README.md:1213`=`не различает две беды` (в теле `:1138`) и +> `docs/architecture/14-api-contract/README.md:1282`=`не различает две беды` (в теле `:1138`) и > `docs/architecture/14-api-contract/openapi.yaml:1679`=`enum: [source_unreadable, not_configured, processing_failed, content_refused]` > (в теле `:1633`). ⇒ сессия фронта, взявшая ту строку как инструкцию, промахнулась бы дважды, и промах выглядел бы > как её собственная ошибка. @@ -508,7 +508,7 @@ ## D39.84 — Стек фронта/платформы запинен, восемь решений владельца ратифицированы, работы движка под фронт получили строки, перевод не индексируется (02.08) -По релею отчёта фронт-сессии (исследование стека закрыто: 15 агентов — 7 направлений × независимые скептики-опровергатели + синтез, версии сверены с вебом 02.08, blocker/major-поправки скептиков записаны исправленными вариантами; артефакты `frontend/docs/STACK_DECISIONS.md` и переписанный `frontend/docs/FRONTEND_SESSION_PROMPT.md` — фронт режется на этапы S0–S7, первая кодовая сессия кончается витриной токенов). Приёмка оркестратора: 10-агентный refute-воркфлоу — ВСЕ несущие бэкенд-утверждения записки CONFIRMED по коду, 8/8 веб-пинов подтверждены первоисточниками (TS7 GA 08.07.2026 БЕЗ программного API до 7.1 → TS 6.0.3 · Vite 8 = Rolldown · Fleet закрыт 22.12.2025 · react-resizable-panels v4 Group/Separator · react-router 8.3 Node≥22.22 — стенд 22.22.3 проходит). **(1) Восемь решений владельца:** рынок глобальный, SEO только Google (Яндекс снят) · выравнивание колонок грубое по единицам экспорта — механизм ГОТОВ (`tmctl export --pairs`, побайтное выравнивание по edit-unit, `internal/pipeline/export.go:89-93`=`PendingUnits int`; уточнение приёмки: draft-only-пайплайн шипит по чанкам, для штатного edit-пайплайна утверждение точно) · подсветка сомнительных мест только блок/глава, внутритекстовые спаны НЕ строятся (у $0-гейтов нет байтовых смещений) · остановка на подписи банка = параметр запуска per-run — ГОТОВО (`--verify-bank`, exit 3, `invocation.go:108`/`main.go:39-46`; галочка формы = передача флага, движок не трогается; в draft-only не останавливает — `mining.go:193-197`) · оплат в MVP нет, денежных полей в UI нет вовсе (расход остаётся телеметрией владельца; потолки/rebill-согласие движка канон §5 не трогает) · десктоп = устанавливаемое PWA первым шагом, Tauri по явным триггерам · макет-инструменты не используются, источник вида = пиксель-замеры референса (Fleet невоспроизводим — растровое совпадение не критерий готовности) · аутентификация: ОДНА серверная сессия, предъявление кукой (браузер) или Bearer (десктоп/CLI), principal только в middleware — вход дизайна П-1 платформенного бэклога. **(2) ПТ-34, продуктовый запрет:** ни один символ пользовательского перевода никогда не попадает на публично индексируемый URL — ни витрин, ни «поделиться», ни публичных ссылок чтения (авторское право + политика Google по масштабируемому машин-контенту бьют по всему домену); носитель — двухконтурный деплой (SPA `app.<домен>` целиком под noindex + статический публичный контур, STACK_DECISIONS §4). **(3) ПТ-21 переформулировано:** якорь чтения = edit-unit экспорта, НЕ «чанк пайплайна» — финальный текст существует только на уровне юнита (~1.9 юнита/главу по замеру фронта; «4276 юнитов» из артефактов репо не выводится — полнокнижной БД не существует, что само подтверждает тезис о ре-ингесте; принято как замер сессии, порядок величин сходится с coldrun-a); читалка обязана жить с «глава = один блок». **(4) Работы движка → строки 99–102** (каждая проверена кодом): 99 пофазный прогресс draft N/M ∥ edit N/M (done = все draft-строки членов + edit-строка, `status.go:328-331/365` — индикатор 0% ВСЮ черновую волну; `chunk_status` уже несёт гранулярность — выводимо без миграции схемы) · 100 персист манифеста глав/чанков + хеш источника (каждый read-вызов заново ингестит и режет исходник — `status.go:177-195`, redrive дважды за вызов; замер фронта 1.42–1.51 с CPU/23 МБ; `chunker_version` уже в снапшоте — `backend/internal/pipeline/render.go:53`=`reclassifies a rune would silently re-chunk the book`) · 101 машиночитаемая таблица подписи банка (кап 20 = только stdout `render.go:98`; полная таблица — текстовый сайдкар `.bank-stop.txt`; JSON нет нигде) · 102 внешний trace-контекст в tmctl (TraceID минтится заново каждым вызовом — `main.go:67-70`; внутри вызова трасса цельная, `request_log.trace_id` персистится). **(5) Противоречие разрешено ВЕРНО:** рекомендация интеграционного агента «долгоживущий демон в backend» отвергнута самой сессией по D39.81 — ратификация удержана, HTTP/SSE/очередь в `platform/`. **(6) Две ловушки эксплуатации подтверждены и заведены входами дизайна (строка 95 · П-1):** подпись термина ≠ UPDATE строки — банк пересобирается каждым прогоном (seedGlossary REPLACES + ReplaceBank DELETE/re-INSERT, `seeding.go:18/110`, `glossary.go:158-162`; контракт подписи пишет в файлы-источники: mined-delta/сид; след затирания — только журнальная строка glossary_revisions, без предупреждения) · порядок деплоя платформы: read-путь схему НЕ мигрирует никогда (`store.go:135-143`, «schema vN … expects vM» в обе стороны; чинит write-команда, годится и `redrive --dry-run`; `tmctl backup` НЕ мигрирует). **(7) Норма зонных бэклогов (решение владельца при лендинге):** фронт и платформа держат СВОИ бэклоги в своих зонах — `frontend/docs/BACKLOG.md` (Ф-1..Ф-6) · `platform/BACKLOG.md` (П-1..П-4); засеяны оркестратором при лендинге переносом уже записанных отложенных решений, дальше ведут зоны; ЕДИНЫЙ бэклог PROGRESS фронт/платформа-строк НЕ принимает — остаётся трекером движка/полигона/доков; движковые стыки фронта (зона «бэкенд») живут в едином (строки 99–102); строки 96/97 переехали в платформенный (П-1/П-2) с ID-надгробиями-указателями (ID стабильны навсегда — внешние ссылки не рвутся). **(8) Лендинг зон** `frontend/` + `platform/` + `.gitignore` (`frontend/references/` — скриншоты вне git, пины замеров сохранены в доках). (02.08.2026, оркестратор №9). ✅ +По релею отчёта фронт-сессии (исследование стека закрыто: 15 агентов — 7 направлений × независимые скептики-опровергатели + синтез, версии сверены с вебом 02.08, blocker/major-поправки скептиков записаны исправленными вариантами; артефакты `frontend/docs/STACK_DECISIONS.md` и переписанный `frontend/docs/FRONTEND_SESSION_PROMPT.md` — фронт режется на этапы S0–S7, первая кодовая сессия кончается витриной токенов). Приёмка оркестратора: 10-агентный refute-воркфлоу — ВСЕ несущие бэкенд-утверждения записки CONFIRMED по коду, 8/8 веб-пинов подтверждены первоисточниками (TS7 GA 08.07.2026 БЕЗ программного API до 7.1 → TS 6.0.3 · Vite 8 = Rolldown · Fleet закрыт 22.12.2025 · react-resizable-panels v4 Group/Separator · react-router 8.3 Node≥22.22 — стенд 22.22.3 проходит). **(1) Восемь решений владельца:** рынок глобальный, SEO только Google (Яндекс снят) · выравнивание колонок грубое по единицам экспорта — механизм ГОТОВ (`tmctl export --pairs`, побайтное выравнивание по edit-unit, `backend/internal/pipeline/export.go:89-93`=`PendingUnits int`; уточнение приёмки: draft-only-пайплайн шипит по чанкам, для штатного edit-пайплайна утверждение точно) · подсветка сомнительных мест только блок/глава, внутритекстовые спаны НЕ строятся (у $0-гейтов нет байтовых смещений) · остановка на подписи банка = параметр запуска per-run — ГОТОВО (`--verify-bank`, exit 3, `invocation.go:108`/`main.go:39-46`; галочка формы = передача флага, движок не трогается; в draft-only не останавливает — `mining.go:193-197`) · оплат в MVP нет, денежных полей в UI нет вовсе (расход остаётся телеметрией владельца; потолки/rebill-согласие движка канон §5 не трогает) · десктоп = устанавливаемое PWA первым шагом, Tauri по явным триггерам · макет-инструменты не используются, источник вида = пиксель-замеры референса (Fleet невоспроизводим — растровое совпадение не критерий готовности) · аутентификация: ОДНА серверная сессия, предъявление кукой (браузер) или Bearer (десктоп/CLI), principal только в middleware — вход дизайна П-1 платформенного бэклога. **(2) ПТ-34, продуктовый запрет:** ни один символ пользовательского перевода никогда не попадает на публично индексируемый URL — ни витрин, ни «поделиться», ни публичных ссылок чтения (авторское право + политика Google по масштабируемому машин-контенту бьют по всему домену); носитель — двухконтурный деплой (SPA `app.<домен>` целиком под noindex + статический публичный контур, STACK_DECISIONS §4). **(3) ПТ-21 переформулировано:** якорь чтения = edit-unit экспорта, НЕ «чанк пайплайна» — финальный текст существует только на уровне юнита (~1.9 юнита/главу по замеру фронта; «4276 юнитов» из артефактов репо не выводится — полнокнижной БД не существует, что само подтверждает тезис о ре-ингесте; принято как замер сессии, порядок величин сходится с coldrun-a); читалка обязана жить с «глава = один блок». **(4) Работы движка → строки 99–102** (каждая проверена кодом): 99 пофазный прогресс draft N/M ∥ edit N/M (done = все draft-строки членов + edit-строка, `status.go:328-331/365` — индикатор 0% ВСЮ черновую волну; `chunk_status` уже несёт гранулярность — выводимо без миграции схемы) · 100 персист манифеста глав/чанков + хеш источника (каждый read-вызов заново ингестит и режет исходник — `status.go:177-195`, redrive дважды за вызов; замер фронта 1.42–1.51 с CPU/23 МБ; `chunker_version` уже в снапшоте — `backend/internal/pipeline/render.go:53`=`reclassifies a rune would silently re-chunk the book`) · 101 машиночитаемая таблица подписи банка (кап 20 = только stdout `render.go:98`; полная таблица — текстовый сайдкар `.bank-stop.txt`; JSON нет нигде) · 102 внешний trace-контекст в tmctl (TraceID минтится заново каждым вызовом — `main.go:67-70`; внутри вызова трасса цельная, `request_log.trace_id` персистится). **(5) Противоречие разрешено ВЕРНО:** рекомендация интеграционного агента «долгоживущий демон в backend» отвергнута самой сессией по D39.81 — ратификация удержана, HTTP/SSE/очередь в `platform/`. **(6) Две ловушки эксплуатации подтверждены и заведены входами дизайна (строка 95 · П-1):** подпись термина ≠ UPDATE строки — банк пересобирается каждым прогоном (seedGlossary REPLACES + ReplaceBank DELETE/re-INSERT, `seeding.go:18/110`, `glossary.go:158-162`; контракт подписи пишет в файлы-источники: mined-delta/сид; след затирания — только журнальная строка glossary_revisions, без предупреждения) · порядок деплоя платформы: read-путь схему НЕ мигрирует никогда (`store.go:135-143`, «schema vN … expects vM» в обе стороны; чинит write-команда, годится и `redrive --dry-run`; `tmctl backup` НЕ мигрирует). **(7) Норма зонных бэклогов (решение владельца при лендинге):** фронт и платформа держат СВОИ бэклоги в своих зонах — `frontend/docs/BACKLOG.md` (Ф-1..Ф-6) · `platform/BACKLOG.md` (П-1..П-4); засеяны оркестратором при лендинге переносом уже записанных отложенных решений, дальше ведут зоны; ЕДИНЫЙ бэклог PROGRESS фронт/платформа-строк НЕ принимает — остаётся трекером движка/полигона/доков; движковые стыки фронта (зона «бэкенд») живут в едином (строки 99–102); строки 96/97 переехали в платформенный (П-1/П-2) с ID-надгробиями-указателями (ID стабильны навсегда — внешние ссылки не рвутся). **(8) Лендинг зон** `frontend/` + `platform/` + `.gitignore` (`frontend/references/` — скриншоты вне git, пины замеров сохранены в доках). (02.08.2026, оркестратор №9). ✅ ## D39.85 — Шов движок↔платформа: индустриальный стандарт ратифицирован — событийный поток, материализация, артефакты; ресёрч записан (02.08) @@ -524,7 +524,7 @@ **2. Приёмка — три адверсариальных воркфлоу-пасса, 0 опровергнутых из 19 подтверждённых** (черновик: 4 линзы×скептики, 2 high — «withheld=текст не выдан» ложно о движке (sanitizer_stripped и c-lite drop отгружают текст под флагом, `disposition.go:96-104`/`export.go:203-215`) и чтения без канала шва (текст юнитов посреди прогона · полный банк — строка 101 это таблица подписи, не экспорт) + 8 medium — сквозной паттерн «инфляция провенанса ✓ на ◆-предложениях»; round-1 дофикс: 6/6 применён, перепроверено кодом, генерённая цепочка подтверждена пере-генерацией байт-в-байт; round-2 свежий взгляд на новые схемы: 5 medium — экспорт ненаблюдаем · promote без dst (движок hard-reject, `memseed.go:84-86`) · закрытый required kind против легального пустого (`memseed.go:323-326`) · самопротиворечие §2.7 · resume после потолка без канала поднятия; все вправлены, включая if/then-констрейнт с честным замером «openapi-typescript игнорирует 3.1-условия» → К-11). Сессия сама завела К-10 (несплитованный счётчик главы) и К-12 (poll vs push экспорта). State выведен из ПАРЫ (вердикт, наличие текста); фразы приложения А — плейсхолдеры (В-3 за владельцем); ПТ-34-инвариант вписан (noindex · no-store · выгрузка только владельцу). -**3. Очередь.** Строка 95 ЗАКРЫТА. **99/100/101/103 перевешены «к подключению фронта» → «скоро, БЛОКЕРЫ контракта»** (аргументы сессии подтверждены кодом: без 100 у главы нет стабильного id — нумерация плотная, `chunker.go:99-105`=`already applied text.NormalizeSource and split chapters`; без 99 индикатор 0% всю черновую волну — `status.go:328-331`; без 101 подписной экран читает сайдкар с капом 20 — `backend/cmd/tmctl/render.go:238`=`capped at 20 rows and ordered least-sure` — ⚠ испр. 16.09: было :98). НОВЫЕ строки: **125** банк-экспорт артефакт (канал `/bank`) · **126** механизм поднятия потолка (платформа; продуктовое слово — К-8) · **127** DDL-коммент `migrate.go:193` устарел (`auto` не пишет никто — фантом утёк во фронт-доки дважды). Счёт скриптом: 103 строки · скоро 21 · бэкенд 58/66. +**3. Очередь.** Строка 95 ЗАКРЫТА. **99/100/101/103 перевешены «к подключению фронта» → «скоро, БЛОКЕРЫ контракта»** (аргументы сессии подтверждены кодом: без 100 у главы нет стабильного id — нумерация плотная, `backend/internal/chunk/chunker.go:99-105`=`already applied text.NormalizeSource and split chapters`; без 99 индикатор 0% всю черновую волну — `status.go:328-331`; без 101 подписной экран читает сайдкар с капом 20 — `backend/cmd/tmctl/render.go:238`=`capped at 20 rows and ordered least-sure` — ⚠ испр. 16.09: было :98). НОВЫЕ строки: **125** банк-экспорт артефакт (канал `/bank`) · **126** механизм поднятия потолка (платформа; продуктовое слово — К-8) · **127** DDL-коммент `migrate.go:193` устарел (`auto` не пишет никто — фантом утёк во фронт-доки дважды). Счёт скриптом: 103 строки · скоро 21 · бэкенд 58/66. **4. Пост-мортем процесса (вопрос владельца «где мы ошиблись на берегу») — три слоя, фиксы в канон.** (а) Ошибка последовательности оркестратора: ратифицирован S3-промт с мёртвым основным сценарием (замок закрыт, промта строки 95 не существовало) — фронт три сессии упирался в один вход. (б) Ревью промта было структурным, не построчным: инструкция «пинг в docs/PROGRESS.md» дословно следовала таблице ролей CLAUDE.md, но противоречила решению владельца 02.08 из шапки зонного журнала. (в) Системная дыра: решения владельца, принятые в зонных сессиях, не доезжают до канона. **Фиксы:** CLAUDE.md — пинг-канал фронта/платформы = ЗОННЫЙ журнал (`frontend/docs/frontend-PROGRESS.md` — переименован по слову владельца, писать туда всем включая оркестратора), не docs/PROGRESS.md; промт оркестратора — два чек-пункта (хендофф-промты сверяются ПОСТРОЧНО: каждая инструкция с путём/каналом вне зоны — против зонной таблицы; при каждом лендинге зоны — свип новых «решений владельца» из зонного журнала на confirm списком); шапка `frontend/docs/BACKLOG.md` разведена «предлагать обязан / ратифицирует оркестратор» (просьба №3 сессии — прежняя формулировка спровоцировала остановку первого захода). @@ -719,7 +719,7 @@ **3. Батч 0.3.0 ЗАКАЗАН (строка 183).** Состав и порядок — §5 отчёта (порядок не косметика: Б-1 модель ошибок ПЕРВОЙ — от словаря кодов зависят Б-2/Б-3/Б-8/Б-14а/§8а); резка — §5а; направления вне батча — хвост §5. Исполнение — ОТДЕЛЬНАЯ сессия по промту (норма D39.120 п.2 / эррата 15.08-в), промт — очередь №17. Правила лендинга (§8б): решения перенесены ЭТОЙ нотой до любой правки спеки · компаньон правится ВМЕСТЕ со спекой (его §5-ответ про волны опровергнут исполнением, Б-0; «канала банка нет» — Б-7а) · канон первым, зеркало фронта отдельным зонным коммитом, cmp-сверка обязательна. -**4. Строки.** 179 ЗАКРЫТА (сессия → доклад → эта ратификация; исполнение — 183). Новые: **183** батч 0.3.0 (носитель состава — research/28 §5) · **184** жанр вон · **185** дописывание глав + гейт молчаливой перекупки · **186** сеть/перф §5б · **187** два лживых комментария движка о `memory_version` (`snapshot.go:188-189` + `membank/memory.go:360-363`=`allKeys = append(allKeys, nk)` — оба описывают до-pack-20 поведение). Владелец-лист: PD-104 и В-10 закрыты; Ф-56/Ф-57/Ф-61/Ф-62 — диспозиции §6 (исполнение батчем); К-13-остаток (описание `:879`) — в батч. +**4. Строки.** 179 ЗАКРЫТА (сессия → доклад → эта ратификация; исполнение — 183). Новые: **183** батч 0.3.0 (носитель состава — research/28 §5) · **184** жанр вон · **185** дописывание глав + гейт молчаливой перекупки · **186** сеть/перф §5б · **187** два лживых комментария движка о `memory_version` (`snapshot.go:188-189` + `backend/internal/membank/memory.go:360-363`=`allKeys = append(allKeys, nk)` — оба описывают до-pack-20 поведение). Владелец-лист: PD-104 и В-10 закрыты; Ф-56/Ф-57/Ф-61/Ф-62 — диспозиции §6 (исполнение батчем); К-13-остаток (описание `:879`) — в батч. **5. Пинги розданы** (§8б п.4/§9): платформе — деньги/надёжность §9 + PD-104 + платформенная половина батча + К-10 «не строить»; фронту — диспозиции четырёх вопросов зоны + мок-находки §9 + перф-входы разморозки. Движковые находки §9 — строки 187 и 185 (ja-мина: новый ruby-алиас двигает `memory_version` → снапшот; громкий `--resnapshot`, не тихая перекупка — уточнение приёмки). (16.08.2026, оркестратор №17) ✅ @@ -802,7 +802,7 @@ **1. Решение (первоисточник — владелец, дословный смысл).** «Фронт морозим ещё дальше. Нам нужно довести бэкенд и платформу до логического какого-то завершения, чтоб они работали и отдавали что-то. А фронт будем накручивать уже на готовую функциональность и спиливать оттуда лишние моки, которые неплохо бы держать где-то на уровне бэкендов или как-то ещё отдельно.» ⇒ **условие разморозки D39.136 п.2 АМЕНДИРОВАНО: лендинг P7 зону БОЛЬШЕ НЕ размораживает**, S5-промт не выдаётся; разморозка — отдельным словом владельца по достижении сквозного пути. Входы первого касания зоны (зеркало 0.3.0 · типы · моки · гейт утечки конвейера · ФС-1..12 · Ф-63/Ф-28 · фразы В-11) не отменены — они ждут РАЗМОРОЗКИ, а не лендинга P7. Направление по мокам: мок-мир фронта уступает реальному источнику (дев-стенд платформы существует с P6, D39.132); форма («на уровне бэкендов или отдельно») — вопрос дизайна при разморозке, сейчас не проектируется. -**2. Инвентарь под этот курс (три читающих аудита №18, всё грунтовано кодом; знание, не заказ — носители названы).** (а) **Движок не умеет отдать книгу:** `tmctl export` пишет только stdout — JSON либо ленту с баннерами `=== CHAPTER n CHUNK m ===` (`backend/cmd/tmctl/render.go:1004`=`=== CHAPTER %d CHUNK %d ===` — ⚠ испр. 16.09: было :477, баннер флагнутого `:502`); ассемблера в файл нет ни в движке, ни в платформе, а `export` возвращает успех даже при 100% pending — «never an exit-2 sentinel … export is an audit projection, not a run verdict» (`backend/cmd/tmctl/render.go:1002`=`pending (not yet translated)` — ⚠ испр. 16.09: было :474-475) — лестницы `run_complete/structurally_complete/publishable` в коде 0 вхождений. Носитель — **строка 49** (этап В D15.2), подтверждена кодом, вес не меняется. (б) **Дыры выдачи молчат в самом тексте** — новая **строка 193**. (в) **Боевой конфиг переводит «голым»** (0 вхождений терминолога/банкноты/майнера/классификатора/голоса/репейра в `configs/pipeline-c1.yaml`, `escalation.budget_usd: 0`): это **строка 140**, и её внесение ратифицированно живёт в wire-батче **182** — одним касанием перед первым платным прогоном; отдельной развилки нет (уточнение владельца 17.08 на вопрос №18). (г) **Масштаб:** жёстких потолков размера в коде нет, но чтения стора беспагинационны под `opTimeout = 10s` — **дописка в строку 54**. (д) **Платформа после P7 — «читаемая», не «законченная»:** экспорт-ручки отложены в P8 явно, денежный контур = холд-с-обрезкой без эскроу (перерасход срезается до холда и остаётся текстом в колонке, `platform/internal/pgstore/credits.go:212-235`), сверки с провайдером нет — **строки 136/137, П-18** зоны. (е) Раскладка бэклога движка по критерию «мешает ли сквозному пути»: 103 строки зоны, блокирующих **18** (+6 спорных), и два тяжелейших — **54** и **16** — не про код вовсе: книгу больше 10 глав и редакторскую волну не гоняли НИ РАЗУ, их закрывает только платный прогон. +**2. Инвентарь под этот курс (три читающих аудита №18, всё грунтовано кодом; знание, не заказ — носители названы).** (а) **Движок не умеет отдать книгу:** `tmctl export` пишет только stdout — JSON либо ленту с баннерами `=== CHAPTER n CHUNK m ===` (`backend/cmd/tmctl/render.go:1034`=`=== CHAPTER %d CHUNK %d ===` — ⚠ испр. 16.09: было :477, баннер флагнутого `:502`); ассемблера в файл нет ни в движке, ни в платформе, а `export` возвращает успех даже при 100% pending — «never an exit-2 sentinel … export is an audit projection, not a run verdict» (`backend/cmd/tmctl/render.go:1032`=`pending (not yet translated)` — ⚠ испр. 16.09: было :474-475) — лестницы `run_complete/structurally_complete/publishable` в коде 0 вхождений. Носитель — **строка 49** (этап В D15.2), подтверждена кодом, вес не меняется. (б) **Дыры выдачи молчат в самом тексте** — новая **строка 193**. (в) **Боевой конфиг переводит «голым»** (0 вхождений терминолога/банкноты/майнера/классификатора/голоса/репейра в `configs/pipeline-c1.yaml`, `escalation.budget_usd: 0`): это **строка 140**, и её внесение ратифицированно живёт в wire-батче **182** — одним касанием перед первым платным прогоном; отдельной развилки нет (уточнение владельца 17.08 на вопрос №18). (г) **Масштаб:** жёстких потолков размера в коде нет, но чтения стора беспагинационны под `opTimeout = 10s` — **дописка в строку 54**. (д) **Платформа после P7 — «читаемая», не «законченная»:** экспорт-ручки отложены в P8 явно, денежный контур = холд-с-обрезкой без эскроу (перерасход срезается до холда и остаётся текстом в колонке, `platform/internal/pgstore/credits.go:212-235`), сверки с провайдером нет — **строки 136/137, П-18** зоны. (е) Раскладка бэклога движка по критерию «мешает ли сквозному пути»: 103 строки зоны, блокирующих **18** (+6 спорных), и два тяжелейших — **54** и **16** — не про код вовсе: книгу больше 10 глав и редакторскую волну не гоняли НИ РАЗУ, их закрывает только платный прогон. **3. Синхронизировано этим же коммитом:** CURRENT-STATE · `README.md` · ПТ-20 реестра требований · пинг в зонный журнал фронта. Очередь №18: приёмка P7 → лендинг **без** S5 и без разморозки; промт бэкенд-пака честности (176 · 181 · 172-г · 187) выдан и ждёт слова владельца на запуск. (17.08.2026, оркестратор №18) ✅ @@ -846,11 +846,11 @@ **1. Решение (владелец, вариант А).** `snapshot_id`/`run_id` на таблице `checkpoints` **сейчас не заводится**; строка 195 как самостоятельная стройка ЗАКРЫТА и переформулирована в ось «идентичность оплаченной работы» дизайн-пака **124** (там же получила номер оси (г)). Пак 124 раньше своих триггеров не форсируется: дописывание глав в текущую книгу (185) · МАСШТАБ · первый внешний потребитель числа согласия. -**2. Чем это обосновано — две посылки оркестратора ОПРОВЕРГНУТЫ (внешний разбор, другое модельное семейство, чтение доков целиком + кода).** (а) **«Колонка превращает вывод в тождество» — НЕВЕРНО.** $0-пере-пин законно переносит строку `chunk_status` на новый снапшот со СТАРОЙ ценой (`internal/pipeline/stagerun.go:96-105`=`the snapshot moved ONLY bec` — `repinned.SnapshotID = snapID`; сверено оркестратором лично), поэтому у честно ре-пиненной строки деньги принадлежат чужому поколению ПО ЗАКОНУ и равенство снапшотов даёт пустоту; ретроактивного бэкфилла нет вовсе (`jobs.snapshot_id` перезаписывается каждым ре-снапшотом) ⇒ вывод-обход остаётся в коде НАВСЕГДА как legacy-ветка, а колонка лишь добавляет рядом второй путь. (б) **«Одна семья, решать разом» — слишком широко.** 49 и 195 отвечают на РАЗНЫЕ вопросы: 49 — «когда переиспользование законно» (гейт трат), 195 — «как честно посчитать будущую покупку» (учёт); общий у них субстрат, не задача. Из строки 124 родня только ось (в), оси (а)/(б) к деньгам отношения не имеют. Деферы D39.31 и гейт спеки D15.2 («онгоинг») не наступили и не отменяются. +**2. Чем это обосновано — две посылки оркестратора ОПРОВЕРГНУТЫ (внешний разбор, другое модельное семейство, чтение доков целиком + кода).** (а) **«Колонка превращает вывод в тождество» — НЕВЕРНО.** $0-пере-пин законно переносит строку `chunk_status` на новый снапшот со СТАРОЙ ценой (`backend/internal/pipeline/stagerun.go:96-105`=`the snapshot moved ONLY bec` — `repinned.SnapshotID = snapID`; сверено оркестратором лично), поэтому у честно ре-пиненной строки деньги принадлежат чужому поколению ПО ЗАКОНУ и равенство снапшотов даёт пустоту; ретроактивного бэкфилла нет вовсе (`jobs.snapshot_id` перезаписывается каждым ре-снапшотом) ⇒ вывод-обход остаётся в коде НАВСЕГДА как legacy-ветка, а колонка лишь добавляет рядом второй путь. (б) **«Одна семья, решать разом» — слишком широко.** 49 и 195 отвечают на РАЗНЫЕ вопросы: 49 — «когда переиспользование законно» (гейт трат), 195 — «как честно посчитать будущую покупку» (учёт); общий у них субстрат, не задача. Из строки 124 родня только ось (в), оси (а)/(б) к деньгам отношения не имеют. Деферы D39.31 и гейт спеки D15.2 («онгоинг») не наступили и не отменяются. **3. Настоящий корень назван (для будущего пака 124).** Не «денежная таблица не помнит конфигурацию», а «в сторе нет первоклассной связи *строка диспозиции ↔ вызовы, которые её оплатили*»: `checkpoints` append-only на жизнь книги, `chunk_status.cost_usd` перезаписывается каждым поколением, и единственный пер-поколенный авторитет — деньги самой строки. Снапшот на чекпоинте — прокси этой связи, а не примитив; класть в схему надо провенанс, а не флаг конфигурации. -**4. Почему срочности нет.** Под контрактом D39.150 («гарантируется НАПРАВЛЕНИЕ ошибки, не точность») ни один исход нынешнего механизма не занижает молча: нормальный случай точен, недостача несётся историческими деньгами С РАСКРЫТИЕМ, пойманный откат откатывается к устаревшей цене ВСЛУХ. Остаточные нарушения контракта чинятся БЕЗ схемы и уже имеют носителей — **197** (прибить гарантии тестами и довести раскрытие до `status`) и **194** (деньги банковых ролей в число согласия). ⚠ Уточнение цены 194, сверенное оркестратором: миграция ей НЕ нужна — `store.RoleSpentUSD` уже ключуется по РОЛИ чекпоинта, а не по колонке класса (`internal/store/ledger.go:105-118`=`RoleSpentUSD sums the settled cost`, докстринг прямо: «a new call class needs no migration to become countable»). (17.08.2026, оркестратор №18) ✅ +**4. Почему срочности нет.** Под контрактом D39.150 («гарантируется НАПРАВЛЕНИЕ ошибки, не точность») ни один исход нынешнего механизма не занижает молча: нормальный случай точен, недостача несётся историческими деньгами С РАСКРЫТИЕМ, пойманный откат откатывается к устаревшей цене ВСЛУХ. Остаточные нарушения контракта чинятся БЕЗ схемы и уже имеют носителей — **197** (прибить гарантии тестами и довести раскрытие до `status`) и **194** (деньги банковых ролей в число согласия). ⚠ Уточнение цены 194, сверенное оркестратором: миграция ей НЕ нужна — `store.RoleSpentUSD` уже ключуется по РОЛИ чекпоинта, а не по колонке класса (`backend/internal/store/ledger.go:105-118`=`RoleSpentUSD sums the settled cost`, докстринг прямо: «a new call class needs no migration to become countable»). (17.08.2026, оркестратор №18) ✅ ## D39.152 — КОНТРАКТ API v0 **0.4.0 РАТИФИЦИРОВАН** (лендинг `6e84574`) (синк с платформой, 14 пунктов; author≠ratifier соблюдён сессией); четыре текстовые правки внесены при ратификации; клейм «ломающая правка одна» верен для типов и неточен поведенчески (20.08). ✅ @@ -950,7 +950,7 @@ **5. Найдено вне заказа и записано строками.** **210** — род НЕ ПРОИЗВОДИТСЯ авто-путём вовсе: доставка построена (`genderConstraintNote`, confirmed-only, директива D39.21 «род должен доезжать»), а данных ей никто не даёт — промпт терминолога возвращает ровно три поля без рода, майнер берёт `gender` только из сида ⇒ на книге без ручного сида ось рода мертва по построению; реестр памяти зовёт это «жалобой №1 читателей MTL». Тем же касанием: `gates.voice` не встречается НИ В ОДНОМ шиппинг-конфиге — прогон с редактурой осей голоса не посчитает, пока гейт не включён. **208** — эскалация на Gemini молча теряет инъекцию банка (пинг полигона, пере-проверен кодом). **209** — риг живых проб не может залогировать тела ни при каких env. -**6. СНЯТА ратификация, которой не требовалось (и это исправление МОЕЙ ошибки причинности).** Строки 203(з)/191(б) я нёс как «D39.144 говорит один ОК, движок держит пер-термный гейт, одно из двух должно уступить — ратификация владельца». Опровергнуто кодом: гейт проверяет полноту не ПОДПИСЕЙ, а ФАЙЛА решений, и ОДИН файл его снимает — `loadMinedDelta` штампует только `Source`, опущенный статус дефолтится в `approved` (`membank/memseed.go:71-74`=`written from model output on EVERY run`), `unsignedEngineSurfaces` выбрасывает лишь `Source=="mined" && Status!="approved"` ⇒ промоутнутые строки остаются сид-поверхностями, дельта пустеет. Обход в платформе — костыль под ОТСУТСТВУЮЩУЮ ДОСТАВКУ, а не под конфликт моделей. Единственный живой блокер темы — владение `book.yaml` (199а). **Урок метода: «два дока противоречат» ≠ «нужна ратификация»; сперва трассируй МЕХАНИЗМ, потом объявляй развилку.** +**6. СНЯТА ратификация, которой не требовалось (и это исправление МОЕЙ ошибки причинности).** Строки 203(з)/191(б) я нёс как «D39.144 говорит один ОК, движок держит пер-термный гейт, одно из двух должно уступить — ратификация владельца». Опровергнуто кодом: гейт проверяет полноту не ПОДПИСЕЙ, а ФАЙЛА решений, и ОДИН файл его снимает — `loadMinedDelta` штампует только `Source`, опущенный статус дефолтится в `approved` (`backend/internal/membank/memseed.go:71-74`=`written from model output on EVERY run`), `unsignedEngineSurfaces` выбрасывает лишь `Source=="mined" && Status!="approved"` ⇒ промоутнутые строки остаются сид-поверхностями, дельта пустеет. Обход в платформе — костыль под ОТСУТСТВУЮЩУЮ ДОСТАВКУ, а не под конфликт моделей. Единственный живой блокер темы — владение `book.yaml` (199а). **Урок метода: «два дока противоречат» ≠ «нужна ратификация»; сперва трассируй МЕХАНИЗМ, потом объявляй развилку.** **7. Три мины формата `mined_delta`, которые обязана знать сессия трубы** (сверено кодом, ни одной не было в докax): копи-паст карты подписи НЕ работает — карта пишется вся со `Status:"auto"`, и это запинено, а `auto` фильтруется как неподписанный ⇒ бесконечная петля; терм БЕЗ `dst` промотить нельзя вовсе — approved с пустым `dst` роняет загрузку сида и срывает прогон, для WHICH-only термов единственный очищающий глагол это ОТКАЗ; хвост реверс-секции ломает «один ОК» на больших книгах — кап 200 режет ПОСЛЕ фильтров, при >200 banknote-поверхностей нужно ceil(N/200) актов. @@ -2906,7 +2906,7 @@ $2.00» не мерил никто), **а утверждение о ПРОШЛО **1. Что заландено.** `33b896b` (платформа: два пина + зонный журнал) · `1cae0ac` (канон) · `f05795e` + `c206067` (пере-наводка трёх якорей, сдвинутых правкой канона). Обе лжи, названные в эррате 06.09-в, сняты; `minimum: 0` возвращён, абзац `Progress` переписан на порядок проверок. -**2. ⭐ ГЛАВНОЕ — НЕ ПРАВКА, А ДЫРА, КОТОРУЮ ОТКРЫЛ ВОПРОС «А ТОЧНО ЛИ НИЧТО ИНОЕ?».** Записав «ноль в `ordered_chapters` ⇒ ре-проход», зона пошла проверять ЭКСКЛЮЗИВНОСТЬ и нашла вторую форму, дающую тот же ноль: `pricing.Quote` клампит заказ всей книги к остатку, поэтому над ПОЛНОСТЬЮ ДОСТАВЛЕННОЙ книгой квотит **ноль глав**, а допуск стора отвергает ноль только при `Resnapshot == false`, которое любое продолжение над сдвинутым банком ставит в `true`. Такой прогон лёг бы с `ceiling_chapters = 0` и был бы **НЕОТЛИЧИМ от ре-прохода на проводе** — полоса `0/1`, `stage: re_pass`, `delivered_chapters: 0`: покупателю показали бы чужую форму работы. ⚠ **Дверь стояла закрытой** — гвард `quote.Chapters == 0` в `runs.Start` (`platform/internal/runs/runs.go:490`=`if quote.Chapters == 0`), после ветви ре-прохода и до взятия холда, — **но его не держал НИ ОДИН тест: снятие гварда проходило батарею зелёной.** Заведён `runs.TestABookWithNothingLeftIsRefusedRatherThanSoldAsAZeroChapterRun`. ⭐ **Проверено мной исполнением, а не отчётом:** мутировал гвард в `if false` — пин КРАСНЕЕТ (`FAIL`, 0.40 с), файл восстановлен байт-в-байт по sha256. Это не гейт, защищающий дефект (`D39.202`). +**2. ⭐ ГЛАВНОЕ — НЕ ПРАВКА, А ДЫРА, КОТОРУЮ ОТКРЫЛ ВОПРОС «А ТОЧНО ЛИ НИЧТО ИНОЕ?».** Записав «ноль в `ordered_chapters` ⇒ ре-проход», зона пошла проверять ЭКСКЛЮЗИВНОСТЬ и нашла вторую форму, дающую тот же ноль: `pricing.Quote` клампит заказ всей книги к остатку, поэтому над ПОЛНОСТЬЮ ДОСТАВЛЕННОЙ книгой квотит **ноль глав**, а допуск стора отвергает ноль только при `Resnapshot == false`, которое любое продолжение над сдвинутым банком ставит в `true`. Такой прогон лёг бы с `ceiling_chapters = 0` и был бы **НЕОТЛИЧИМ от ре-прохода на проводе** — полоса `0/1`, `stage: re_pass`, `delivered_chapters: 0`: покупателю показали бы чужую форму работы. ⚠ **Дверь стояла закрытой** — гвард `quote.Chapters == 0` в `runs.Start` (`platform/internal/runs/runs.go:513`=`if quote.Chapters == 0`), после ветви ре-прохода и до взятия холда, — **но его не держал НИ ОДИН тест: снятие гварда проходило батарею зелёной.** Заведён `runs.TestABookWithNothingLeftIsRefusedRatherThanSoldAsAZeroChapterRun`. ⭐ **Проверено мной исполнением, а не отчётом:** мутировал гвард в `if false` — пин КРАСНЕЕТ (`FAIL`, 0.40 с), файл восстановлен байт-в-байт по sha256. Это не гейт, защищающий дефект (`D39.202`). **3. Норма авторства (`D39.211` п.3а) исполнена ПЕРВЫЙ РАЗ, и её польза оказалась не той, что ожидалась.** Ожидалась защита от лжи в моём тексте; фактически она заставила зону проверить утверждение, которое **мы оба считали очевидным**, и там нашлась дыра п.2. ⇒ ценность нормы — не в редактуре, а в том, что утверждение о ЗНАЧЕНИИ обязано пройти через того, у кого есть прогон, и он спрашивает «а полон ли перечень», чего автор прозы не спрашивает. @@ -2914,7 +2914,7 @@ $2.00» не мерил никто), **а утверждение о ПРОШЛО **5. Нижняя граница НЕ снята, и довод зоны сильнее моего.** Я предлагал рассмотреть снятие («раз `null` уже отдельный случай»). Зона отказалась: **ноль — это ПОЛ, а не сегодняшнее наименьшее**; отрицательное отвергает сам допуск стора, и схема теперь говорит вслух то, что сервис и так гарантирует. Снять границу значило бы разрешить в контракте то, чего сервис не примет. -**6. То же расщепление в соседних полях — искано МЕХАНИЧЕСКИ, а не глазами.** Сканер зоны по **193** свойствам схем канона (⚠ **испр. 06.09: САМОГО СКАНЕРА В РЕПОЗИТОРИИ НЕТ**, и потому ни «193», ни «семь флагов» не воспроизводимы; агент верификатора, пере-выполнив три правила по их описанию, получил ДВЕНАДЦАТЬ — расхождение говорит не «зона ошиблась», а «правила описаны недостаточно, чтобы их повторить». Строка 317), три правила (граница `> 0` при прозе о нуле · проза обещает `null`, тип не допускает · тип допускает `null`, проза молчит): семь флагов, шесть — ложные срабатывания эвристики (⚠ **испр. эрратой 06.09-ж: ложных ПЯТЬ**, они названы поимённо в зонном журнале, и 5+2=7; тело ноты по `D23.3` не переписывается — это маркер), настоящих два. **(а)** `OrderOptions.structure` — **зеркало моего случая**: тип допускает `null`, а проза не говорила, когда он бывает и что значит, — генератор знал, человек нет. Смысл дописан из пина (`httpapi.TestABookWhoseCutCannotBeSoldAgainstOffersCharactersAndSaysSo`, `platform/internal/httpapi/v0.go:313-315`=`null when no manifest`): **`null` — не четвёртый вид разреза**; `none` — книга, на которую движок посмотрел и нашёл одну главу, `null` — книга, которую ещё не спрашивали. **(б)** `Chapter.number` — уже объявлено эрратой 05.09 в самом поле; зона сверила, что довод эрраты ЖИВ (`ingest.Whole()` по-прежнему отвергает `c.Number < 1`), и не трогала. +**6. То же расщепление в соседних полях — искано МЕХАНИЧЕСКИ, а не глазами.** Сканер зоны по **193** свойствам схем канона (⚠ **испр. 06.09: САМОГО СКАНЕРА В РЕПОЗИТОРИИ НЕТ**, и потому ни «193», ни «семь флагов» не воспроизводимы; агент верификатора, пере-выполнив три правила по их описанию, получил ДВЕНАДЦАТЬ — расхождение говорит не «зона ошиблась», а «правила описаны недостаточно, чтобы их повторить». Строка 317), три правила (граница `> 0` при прозе о нуле · проза обещает `null`, тип не допускает · тип допускает `null`, проза молчит): семь флагов, шесть — ложные срабатывания эвристики (⚠ **испр. эрратой 06.09-ж: ложных ПЯТЬ**, они названы поимённо в зонном журнале, и 5+2=7; тело ноты по `D23.3` не переписывается — это маркер), настоящих два. **(а)** `OrderOptions.structure` — **зеркало моего случая**: тип допускает `null`, а проза не говорила, когда он бывает и что значит, — генератор знал, человек нет. Смысл дописан из пина (`httpapi.TestABookWhoseCutCannotBeSoldAgainstOffersCharactersAndSaysSo`, `platform/internal/httpapi/v0.go:336-336`=`null when no manifest`): **`null` — не четвёртый вид разреза**; `none` — книга, на которую движок посмотрел и нашёл одну главу, `null` — книга, которую ещё не спрашивали. **(б)** `Chapter.number` — уже объявлено эрратой 05.09 в самом поле; зона сверила, что довод эрраты ЖИВ (`ingest.Whole()` по-прежнему отвергает `c.Number < 1`), и не трогала. **7. ⚠ НАХОДКА О СПОСОБЕ ПЕРЕДАЧИ, отданная зоной мне и записываемая здесь: НОМЕР СТРОКИ, ПОСЛАННЫЙ СООБЩЕНИЕМ, ПРОТУХАЕТ ОТ СЛЕДУЮЩЕЙ ПРАВКИ ТОГО ЖЕ ФАЙЛА И НИЧЕМ НЕ СТОРОЖИТСЯ.** Замерено на себе: зона прислала три номера для чужих якорей, потом дописала в канон два блока — и все три уехали ровно на четыре строки. **Я поймал это только потому, что пере-считал сам**, а не взял из письма; зона поймала одновременно и прислала поправку. ⇒ **правило: номер строки из чужого письма НЕ переносится в док — переносится ТОКЕН, а номер пере-считывается линтом в момент правки.** Это тот же класс, что «замер, снятый до правки» (`D39.209`): цифра верна на момент отправки и лжёт на момент применения, и ничто в форме сообщения об этом не сигналит. @@ -3540,7 +3540,7 @@ bought NOTHING». ⇒ **`tmctl manifest` есть НИЖНЯЯ граница, **3. ⛔ АКТ `D39.232` ОБЪЯВИЛ СТРОКИ ЗАКРЫТЫМИ И ОСТАВИЛ ИХ В ТАБЛИЦЕ.** Пункт 1 писал «закрыты строки 360 и 369, попутно 78» — и все три сутки простояли в бэклоге живыми. Это ровно та норма, которую эта же смена записала себе в ролевой промт («сверка носителей — тем же движением, что и акт»), и она не сработала на собственном акте автора. Пере-снято и закрыто сегодня: **78** (оплаченный `2xx` с нечитаемым телом теперь пишет СВОЮ строку `request_log` и помечается оценкой — `backend/internal/pipeline/cutcall.go:154`=`rl.Estimated, rl.EstTokens = cost > 0`), **369** (дедлайн выводится из бюджета вызова — `backend/internal/llm/attemptcut.go:269`=`func (p RetryProfile) deriveDeadline`, и эскалация, удвоившая `max_tokens`, удваивает время), **360** (предмет пака). Эррата 10.09-б в шапке. -**4. И ЧЕТВЁРТАЯ строка, закрытая тем же паком, которую не заметил никто, — 331** (движковая половина `PD-441`). Её условие закрытия — «сеттл оценки при отмене ушедшего вызова + публикация оценочных строк в `status --json`» — исполнено обеими половинами: сеттл — `cutcall.go`, публикация — `backend/internal/pipeline/status.go:942`=`rep.EstimatedRows, rep.EstimatedUSD = estimatedSpend(usage, committed)`, и оператору она печатается (`backend/cmd/tmctl/render.go:448`=`estimated-cost rows:`). ⚠ **С НАЗВАННЫМ СУЖЕНИЕМ:** различитель строки был «факт УХОДА запроса», а дерево книжит по «провайдер ОТВЕТИЛ 2xx», что уже; направление сужения — недосчёт, ратифицированный `D39.196` п.2а, и остаток несёт строка **376**. Платформенная половина «сказать больше, чем ≥» — строка **382**. +**4. И ЧЕТВЁРТАЯ строка, закрытая тем же паком, которую не заметил никто, — 331** (движковая половина `PD-441`). Её условие закрытия — «сеттл оценки при отмене ушедшего вызова + публикация оценочных строк в `status --json`» — исполнено обеими половинами: сеттл — `cutcall.go`, публикация — `backend/internal/pipeline/status.go:942`=`rep.EstimatedRows, rep.EstimatedUSD = estimatedSpend(usage, committed)`, и оператору она печатается (`backend/cmd/tmctl/render.go:478`=`estimated-cost rows:`). ⚠ **С НАЗВАННЫМ СУЖЕНИЕМ:** различитель строки был «факт УХОДА запроса», а дерево книжит по «провайдер ОТВЕТИЛ 2xx», что уже; направление сужения — недосчёт, ратифицированный `D39.196` п.2а, и остаток несёт строка **376**. Платформенная половина «сказать больше, чем ≥» — строка **382**. **5. Инвентарь живых доков (ответ на вопрос владельца «доки актуализированы?»).** Прогнан по норме ролевого промта — ВСЕ живые доки против текущего состояния, не только тронутые сменой; прибор прочёл **94** живых `.md` вне архива. Найдено и исправлено: `docs/architecture/15-money-path.md` в ТРЁХ местах утверждал закрытый канал как открытый (пункт маршрута, строка таблицы долга, список бэклог-строк) — переписаны, и туда же внесена НОВАЯ денежная граница пака (доставка · класс `attempt_timeout` без ретрая · дедлайн из бюджета · резюм не перепокупает). Проверено и расхождений НЕ найдено: реестр требований против брифа владельца (V0–V6 разобраны все; бриф не менялся с 24.08 — контроль по `ls`), таблица активных промтов, шапка-таблица `09-target-architecture.md` (транспорт в её слои не входит), `docs/research/21` (несёт ⚠-баннер о вердикте). ⚠ Остаток, который чинить НЕ мне: 13 битых `file:line`-якорей линтера — все в чужих зонах (`backend/docs`, `docs/experiments`, `frontend/docs`) либо уехали не от моих правок; адреса пере-сняты и оставлены пингами в зонных журналах. @@ -3554,7 +3554,7 @@ bought NOTHING». ⇒ **`tmctl manifest` есть НИЖНЯЯ граница, **(в)** `Money` на исходе `stopped` едет ВСЕГДА, а не только когда прежде сработал потолок (сегодня — только тогда: `backend/internal/pipeline/events.go`, греп `ceilingSaid`). Вопрос покупателя «что я купил» от причины остановки не зависит. **(г)** ЗАПАСНОЙ КАНАЛ — обязанность ПЛАТФОРМЫ, не движка: у прогона, убитого по грейсу, кадра `finished` нет вовсе (его отсутствие само по себе значимо), а `status --json` числа несёт всегда ⇒ платформа читает оба. -**2. ⛔ ЭРРАТА К СОБСТВЕННОМУ АКТУ: `D39.234` п.1б назвал МЕХАНИЗМ, который ратифицированных ~20 минут НЕ ДАЁТ.** Слово владельца («одна летящая попытка, ~20 минут приемлемо») в силе и не меняется. Неверна была моя скобка «отказ на пер-вызовном гейте резервации». Замер: резервация берётся ОДИН раз на `runAttempt` (`backend/internal/pipeline/stagerun.go:477`=`finish string`, `admit` на `:570`), а `client.Complete` (`:699`) держит под ней ВСЮ цепочку ретраев — это дословно говорит комментарий самого кода: «This timer wraps client.Complete — the WHOLE retry chain» (`stagerun.go:984`); `retryLoop` между попытками смотрит только `ctx.Err()` (`backend/internal/llm/httpllm.go:197`=`owedCut = moreOwed(owedCut, err)`). ⇒ гейт ограничивает ожидание ЦЕПОЧКОЙ (~64 мин), а не попыткой (~20). Чтобы слово владельца стало правдой, транспорт обязан видеть мягкую остановку МЕЖДУ попытками и в бэкоффе — это заказано §4.2 движкового промта. ⭐ Нашёл не я: моя механика прошла в акт непроверенной, и её опроверг чтением кода старший коллега, которого я позвал ревьюить уже написанный промт. +**2. ⛔ ЭРРАТА К СОБСТВЕННОМУ АКТУ: `D39.234` п.1б назвал МЕХАНИЗМ, который ратифицированных ~20 минут НЕ ДАЁТ.** Слово владельца («одна летящая попытка, ~20 минут приемлемо») в силе и не меняется. Неверна была моя скобка «отказ на пер-вызовном гейте резервации». Замер: резервация берётся ОДИН раз на `runAttempt` (`backend/internal/pipeline/stagerun.go:420`=`finish string`, `admit` на `:570`), а `client.Complete` (`:699`) держит под ней ВСЮ цепочку ретраев — это дословно говорит комментарий самого кода: «This timer wraps client.Complete — the WHOLE retry chain» (`stagerun.go:984`); `retryLoop` между попытками смотрит только `ctx.Err()` (`backend/internal/llm/httpllm.go:197`=`owedCut = moreOwed(owedCut, err)`). ⇒ гейт ограничивает ожидание ЦЕПОЧКОЙ (~64 мин), а не попыткой (~20). Чтобы слово владельца стало правдой, транспорт обязан видеть мягкую остановку МЕЖДУ попытками и в бэкоффе — это заказано §4.2 движкового промта. ⭐ Нашёл не я: моя механика прошла в акт непроверенной, и её опроверг чтением кода старший коллега, которого я позвал ревьюить уже написанный промт. **3. Два парных промта выданы.** `docs/BACKEND_SOFT_STOP_SESSION_PROMPT.md` (строки **381** · **382** · **377** · **372** · **375** · **379**а) и `docs/PLATFORM_SOFT_STOP_SESSION_PROMPT.md`. Порознь НЕ лендятся (`D39.234` п.2). Движковый идёт ОДНОЙ сессией в ДВА лендинга с заранее объявленной линией (резать на две сессии нельзя: §4.2 и §4.8 правят одну функцию `retryLoop`); кончившийся контекст после первого лендинга объявлен штатным исходом. ⚠ Обе редакции промтов правились ДО выдачи по ревью: в движковом восемь мест, в платформенном шесть — включая ДВЕ мои прямые лжи («латч не остановит банковые проходы» — они идут через тот же гейт; «`cancelled` не может затереть настоящую причину» — может, апсерт по ключу стадии ЗАМЕНЯЕТ строку). @@ -4038,7 +4038,7 @@ bought NOTHING». ⇒ **`tmctl manifest` есть НИЖНЯЯ граница, **10. ЯКОРНЫЙ ИТОГ ЛЕНДИНГА — СНЯТ КОНТРОЛЕМ, А НЕ ПАМЯТЬЮ.** На развёртке HEAD (`af88680`) линтер давал **27** протухших якорей, после лендинга — **8**, новых **ноль**. Разница разложена: четыре уехали вместе с промтом в `archive/**` (не проверяется), четырнадцать перенаведены по финальному дереву, один переписан здесь. Остаток назван поимённо, и каждый — с причиной, по которой НЕ тронут: - `docs/architecture/05-decisions-log.md:43`=`Эррата 28.08-к` — эррата 28.08-к в ШАПКЕ этого журнала; шапка сама объявляет форму якорей в телах не подлежащей правке и велит читать с поправкой. Цель «seed-FILE edit» жива на `backend/internal/pipeline/status.go:1042`=`a seed-FILE edit not yet re-run is NOT reflected here` и `:1201`. - `docs/experiments/23-editor-tier.md` — четыре якоря ЗАМОРОЖЕННОГО отчёта эксперимента. Замороженному отчёту положен штамп коммита, а не перенаведённые якоря: иначе улику правят задним числом. -- Три якоря в доках фронта — ЧУЖАЯ зона. Заведён ряд **457**, и обе уехавшие цели названы в нём готовыми адресами (`docs/architecture/14-api-contract/README.md:1138`=`не различает две беды`, `docs/architecture/14-api-contract/openapi.yaml:1633`=`enum: [source_unreadable, not_configured, processing_failed, content_refused]`), потому что сами цели — в МОЕЙ зоне. +- Три якоря в доках фронта — ЧУЖАЯ зона. Заведён ряд **457**, и обе уехавшие цели названы в нём готовыми адресами (`docs/architecture/14-api-contract/README.md:1282`=`не различает две беды`, `docs/architecture/14-api-contract/openapi.yaml:1679`=`enum: [source_unreadable, not_configured, processing_failed, content_refused]`), потому что сами цели — в МОЕЙ зоне. - `docs/experiments/00-provider-quirks.md:169`=`ТОТ ЖЕ ДЕФЕКТ ЖИВЁТ В ДВИЖКЕ` починен **не токеном**: там цель не «уехала», а описанное состояние ИСЧЕЗЛО — `toOpenAIMessages` перестроена 28.08 и склейку теперь делает. Подставить найденный в цели токен значило бы подгонку под зелень; адрес переведён в ИСТОРИЧЕСКИЙ, живой стоит строкой ниже (`backend/internal/llm/httpllm.go:766`=`func toOpenAIMessages`). ⛔ **МОЯ ОШИБКА ПРИБОРА, снятая замером (и она стоила бы ложной находки о чужой зоне).** Сверив свои восемь со списком HEAD, я насчитал ДВА новых якоря во фронте и готов был назвать их следствием лендинга. Замер сказал обратное: цели БАЙТ-ИДЕНТИЧНЫ дереву и HEAD — `14-api-contract/README.md` 1608 строк в обеих, `openapi.yaml` 3497 в обеих, токены на `:1138` и `:1633`. Новыми они не были: **линтер капит список, и контрольный прогон напечатал 25 строк из 27**. Класс — «список, из которого делаешь утверждение, не режь», с поворотом: резал не я, резал ПРИБОР, а выглядело это как факт о дереве. ⇒ у обрезанной выдачи спрашивай знаменатель даже тогда, когда обрезал её не ты. @@ -4059,7 +4059,7 @@ bought NOTHING». ⇒ **`tmctl manifest` есть НИЖНЯЯ граница, **Что принято.** 29 путей в `backend/` (14 изменённых + 15 новых) плюс отчёт в `docs/PROGRESS.md`. Пак **$0**: платных вызовов моделей ноль, и это проверено не по отчёту (п.2). Сессия `textmachine-main-11`, промт `BACKEND_BANK_ZERO_LAYER_SESSION_PROMPT.md`, выдан по слову владельца «Выдавай». -**1. Что построено.** Книга, у чьей ЦЕЛИ нет строк инъекции, больше не покупает банк-роли молча: отказ стоит в `openRunner` до первого платного вызова (`backend/internal/pipeline/runner.go:389`=`checkInjectionTexts`), судит ПОЛНОТУ рендерящихся ключей, а не заголовок, и закрывает **пять** арм, а не три, как заказывал промт — сверх гейтов терминологии и банкноты это сид, деривируемый путь мината-дельты и авто-банк, оставленный прошлым прогоном в сторе. Отказ роли перестал ехать редактору законом: отказ прозой и сентинел идут в `Declined`, мусор без букв — в `Unanswered`, реестр отказных фраз лендится ПУСТЫМ и запинен пустым. Построены два предиката «спорный» (pre-call и post-call) с заданными знаменателями и $0-харнесс реплея (`backend/cmd/tmbankprobe/`). +**1. Что построено.** Книга, у чьей ЦЕЛИ нет строк инъекции, больше не покупает банк-роли молча: отказ стоит в `openRunner` до первого платного вызова (`backend/internal/pipeline/runner.go:401`=`checkInjectionTexts`), судит ПОЛНОТУ рендерящихся ключей, а не заголовок, и закрывает **пять** арм, а не три, как заказывал промт — сверх гейтов терминологии и банкноты это сид, деривируемый путь мината-дельты и авто-банк, оставленный прошлым прогоном в сторе. Отказ роли перестал ехать редактору законом: отказ прозой и сентинел идут в `Declined`, мусор без букв — в `Unanswered`, реестр отказных фраз лендится ПУСТЫМ и запинен пустым. Построены два предиката «спорный» (pre-call и post-call) с заданными знаменателями и $0-харнесс реплея (`backend/cmd/tmbankprobe/`). **2. Чем я это проверил — пять приборов, каждый назван.** - **Батарея на замороженной копии:** `MAKE_EXIT=0`, линтер `0 issues.`, **`go list ./...` 24 = 24 вердикта**, `comm` пуст в обе стороны, `FAIL` 0. ⚠ SKIP 9 против её 4 — разрыв объяснён и не в её пользу и не против: пять лишних скипов (`TestCheckerLabels*`, `TestK6*`, оракул улик) — свойство ВЫНЕСЕННОЙ копии, у которой нет размеченного корпуса. ⇒ **батарея на копии вне репозитория слабее той же батареи в дереве на пять лейбловых харнессов**, и это стоит знать всякой будущей приёмке. @@ -4124,7 +4124,7 @@ bought NOTHING». ⇒ **`tmctl manifest` есть НИЖНЯЯ граница, **2. ПЕРЕНОС БАНКА В ПРОДОЛЖЕНИЕ — СУЩЕСТВУЮЩИМ МЕХАНИЗМОМ, НОВЫХ СУЩНОСТЕЙ НЕ ВВОДИТЬ.** Слово 17.09: «Давай не вводить новые сущности, банк который подписан или автоподписан и который переносится на следующую книгу или следующие главы идет просто вот как текущий банк… не вводить лишний код». ⇒ **предложение третьего источника (отдельный слой «унаследованное») ОТКЛОНЕНО.** Сверено деревом: механизм есть — авто-банк пишется той же сид-YAML схемой, на которой говорит весь конвейер, и его -доккомментарий прямо называет цель «строку можно перенести в дельту копипастой» (`backend/internal/pipeline/mining.go:791`=`as the same seed-YAML`); +доккомментарий прямо называет цель «строку можно перенести в дельту копипастой» (`backend/internal/pipeline/mining.go:822`=`as the same seed-YAML`); загрузчиков под неё шесть в `membank`. ⚠ **Ограничение, которое из-за этого наследуется и должно быть названо, а не обойдено:** термин, попавший в сид, дверью правок банка не решается (`backend/internal/membank/decisions.go:194`=`A term of the seed cannot be decided here`) ⇒ перенесённая строка правится тем же путём, каким правится сид сегодня. Ряд **461** пере-формулирован под это решение. @@ -4148,7 +4148,7 @@ bought NOTHING». ⇒ **`tmctl manifest` есть НИЖНЯЯ граница, **6. КОГДА ТВЕРДЕЕТ НАРЕЗКА — ответ владельца 17.09 и поправка к нему.** Слово: «когда черновики отработали и разметили главы. Это то у нас нормально построено?» ⇒ построено, но РАНЬШЕ, чем предполагает формулировка: главы размечает сегментатор ИСТОЧНИКА на интейке по маркерам из данных языка, а грамматику глав прогон -резолвит на старте (`backend/internal/pipeline/runner.go:456`=`resolves the book's chapter grammar`); черновики глав НЕ размечают и к моменту их работы крой уже +резолвит на старте (`backend/internal/pipeline/runner.go:468`=`resolves the book's chapter grammar`); черновики глав НЕ размечают и к моменту их работы крой уже существует. Версия структуры входит в ключ кроя вместе с версиями языкового пакета, встроенных данных и нормализации (`backend/internal/pipeline/manifest.go:282`= `lang.EmbeddedVersion(), Norm: text.NormVersion()`). ⇒ вопрос «когда твердеет» остаётся открытым в другой форме: не «когда размечаются главы», а **с какого момента перекрой запрещён или платен**, и это решение владельца по-прежнему не получено. diff --git a/docs/architecture/13-tech-debt-anchors.md b/docs/architecture/13-tech-debt-anchors.md index c4737a19..14c051a7 100644 --- a/docs/architecture/13-tech-debt-anchors.md +++ b/docs/architecture/13-tech-debt-anchors.md @@ -129,7 +129,7 @@ Go-гейт стоит на v1 без поглощения кавычек: `back ### [CONFIRMED_LOST] (4) Golden-счётчики голоса (дописка 41): в capture.golden voice-счётчиков нет -Подтверждено отсутствие: `grep -c -i voice backend/internal/pipeline/testdata/golden/capture.golden` = 0 (ни одного вхождения); в testdata/golden/pipeline.yaml ключа `voice:` нет (grep пуст) — гейт opt-in OFF по умолчанию (`backend/internal/config/pipeline.go:315`=`type VoiceGate struct` — ⚠ испр. 16.09: было :279-281), фикстура его не включает, значит golden не пинит ни n_voice_flags/voice_detail, ни VoiceFlags/SpoilerLeaks (quality.go:269-278, 436 — счётчики существуют в коде, но в golden-контур не заведены). Хвост D39.58 реален и жив. +Подтверждено отсутствие: `grep -c -i voice backend/internal/pipeline/testdata/golden/capture.golden` = 0 (ни одного вхождения); в testdata/golden/pipeline.yaml ключа `voice:` нет (grep пуст) — гейт opt-in OFF по умолчанию (`backend/internal/config/pipeline.go:381`=`type VoiceGate struct` — ⚠ испр. 16.09: было :279-281), фикстура его не включает, значит golden не пинит ни n_voice_flags/voice_detail, ни VoiceFlags/SpoilerLeaks (quality.go:269-278, 436 — счётчики существуют в коде, но в golden-контур не заведены). Хвост D39.58 реален и жив. ### [CONFIRMED_LOST] (5) Слот инъекции голоса (дописка 24): инъекционная половина построена условно и НЕ включена, оба провода @@ -174,7 +174,7 @@ Go-гейт стоит на v1 без поглощения кавычек: `back ### Б-106 — тело строки 106 на 09.08.2026 -**Слой 3 НЕ достроен: контракт редактора = full-regen, а целевой — узкие мандаты + ДИФФЫ.** `docs/architecture/09-target-architecture.md:111`=`узкие мандаты + диффы` дословно: «research/07 предписывает узкие мандаты + диффы, а не полную перегенерацию — сейчас ровно наоборот (4 мандата, full-regen), что и породило хвост инверсий» (⚠ прежний голый якорь `:121-126` съехал на слой 5/6); целевой контракт — редактор правит anchored search/replace-спанами с детерминированным Go-apply (ограниченный blast-radius, дешевле, аудируемо, структурно omission-safe). Пак-16 построил петлю РЕМОНТА (починка флагнутого спана постфактум), а не diff-контракт роли ⇒ это разные вещи. **Строки не было ни одной** — дыра учёта, найденная сверкой 02.08 (грепы `diff`/`мандат`/`span` по бэклогу = 0). Смежное: арм Q4b «diff-fidelity+style редактор» (`exp15:108`) не прогонялся НИ РАЗУ, хотя трек B объявлен завершённым. **ЭМПИРИКА ПОЛУЧЕНА (D39.108, экспы 19/20):** дифф-контракт механически исправен (комплаенс 0.966 на боевой единице, аппликатор 15/15), но по замороженному гейту Q4b диффы ОТЛОЖЕНЫ (fidelity-trap 11/12; дороже full-regen на dspro×ru ×2.5–2.9 при невскрытой причине; D21.4/D21.10 закрыты явно). **Эксп-21 (D39.117):** полный regen выигрывает у точечного ремонта (`C2/A_law` −3.75 Holm 0.0006) — довод против диффов, НЕ опровержение: чинил дешёвый фиксер по предложению, а не сильный редактор узким мандатом; гейт «перепроверить до стройки» в силе, приоритет понижен +**Слой 3 НЕ достроен: контракт редактора = full-regen, а целевой — узкие мандаты + ДИФФЫ.** `docs/architecture/09-target-architecture.md:115`=`узкие мандаты + диффы` дословно: «research/07 предписывает узкие мандаты + диффы, а не полную перегенерацию — сейчас ровно наоборот (4 мандата, full-regen), что и породило хвост инверсий» (⚠ прежний голый якорь `:121-126` съехал на слой 5/6); целевой контракт — редактор правит anchored search/replace-спанами с детерминированным Go-apply (ограниченный blast-radius, дешевле, аудируемо, структурно omission-safe). Пак-16 построил петлю РЕМОНТА (починка флагнутого спана постфактум), а не diff-контракт роли ⇒ это разные вещи. **Строки не было ни одной** — дыра учёта, найденная сверкой 02.08 (грепы `diff`/`мандат`/`span` по бэклогу = 0). Смежное: арм Q4b «diff-fidelity+style редактор» (`exp15:108`) не прогонялся НИ РАЗУ, хотя трек B объявлен завершённым. **ЭМПИРИКА ПОЛУЧЕНА (D39.108, экспы 19/20):** дифф-контракт механически исправен (комплаенс 0.966 на боевой единице, аппликатор 15/15), но по замороженному гейту Q4b диффы ОТЛОЖЕНЫ (fidelity-trap 11/12; дороже full-regen на dspro×ru ×2.5–2.9 при невскрытой причине; D21.4/D21.10 закрыты явно). **Эксп-21 (D39.117):** полный regen выигрывает у точечного ремонта (`C2/A_law` −3.75 Holm 0.0006) — довод против диффов, НЕ опровержение: чинил дешёвый фиксер по предложению, а не сильный редактор узким мандатом; гейт «перепроверить до стройки» в силе, приоритет понижен ### Б-108 — тело строки 108 на 09.08.2026 diff --git a/docs/architecture/14-api-contract/README.md b/docs/architecture/14-api-contract/README.md index a0a55e5c..999d607f 100644 --- a/docs/architecture/14-api-contract/README.md +++ b/docs/architecture/14-api-contract/README.md @@ -180,7 +180,7 @@ cmp-сверка обязательна (D39.138 п.3). (`platform/internal/books/books.go:118-121`=`books: the intake form is not usable`) и равенство `source == target` не проверяет никто, а пар-промпты в репозитории есть ТОЛЬКО для `zh-ru`; отсутствие промпта пары — жёсткая ошибка конфигурации, не деградация («no prompt for pair %q role %q … never silently substitute another -pair's conventions», `backend/internal/config/pipeline.go:1065`=`no prompt for pair` — ⚠ испр. 17.09: было :1038, ранее :952-956). До 0.3.0 книга в +pair's conventions», `backend/internal/config/pipeline.go:1169`=`no prompt for pair` — ⚠ испр. 17.09: было :1038, ранее :952-956). До 0.3.0 книга в неподдерживаемой паре принималась, ложилась на диск, разбиралась, проводила пользователя через денежный экран — и умирала на старте прогона без причины. Отсюда `Capabilities.language_pairs[]` со статусом и код отказа на интейке (`errors[].code: unsupported_pair`). @@ -305,7 +305,7 @@ SQL и до 0.3.0 не отдавалась: `b.chapter_count - (select count(*) Флагнутый юнит легально приходит С ТЕКСТОМ в двух случаях: косметическая зачистка санитайзера («the chunk is NOT lost — the cleaned text is committed as the export», `backend/internal/pipeline/disposition.go:160`=`the cleaned text is committed as the export` — ⚠ испр. 16.09: было :96-104) и -c-lite member-drop (`pipeline/export.go:203-207`=`if exp.GhostRows > 0`). Поэтому состояние выводится из ПАРЫ (вердикт + +c-lite member-drop (`backend/internal/pipeline/export.go:203-207`=`if exp.GhostRows > 0`). Поэтому состояние выводится из ПАРЫ (вердикт + наличие финального текста): флаг+текст → `translated` с замечанием; флаг+пусто → `withheld`. Причина флага при этом ПРОИЗВОЛЬНА («единственный легальный случай» снято ревью round-2): при c-lite @@ -356,7 +356,7 @@ drop юнит несёт причину ПЕРВОГО выпавшего чле **Фантом `auto` в провенансе убран ещё в 0.2.0:** комментарий схемы движка (`migrate.go:193`: `seed|ruby|auto`) устарел — `"auto"` пишет СТАТУС, не провенанс (`backend/internal/membank/memseed.go:424`=`Status: "auto", Source: "ruby"` — ⚠ испр. 16.09: было :328: -`Status:"auto", Source:"ruby"`), майнинг ставит `mined` (`backend/internal/pipeline/mining.go:763`=`e.Source = "mined"` — ⚠ испр. 16.09: было :424). +`Status:"auto", Source:"ruby"`), майнинг ставит `mined` (`backend/internal/pipeline/mining.go:794`=`e.Source = "mined"` — ⚠ испр. 16.09: было :424). **Ложный друг `source`.** У движка колонка `source` — это ПРОВЕНАНС. Поэтому в контракте провенанс зовётся `origin`, формы термина — `src`/`dst`, а имя `source` в схеме банка не используется вовсе. @@ -372,7 +372,7 @@ drop юнит несёт причину ПЕРВОГО выпавшего чле ### 2.9. Подпись — набор решений — ✓ выведено -Конвейер заменяет банк целиком (`store/migrate.go:169-170`=`aggregation/classification live in the pipeline`), поэтому `PATCH /term/{id}` молча не +Конвейер заменяет банк целиком (`backend/internal/store/migrate.go:169-170`=`aggregation/classification live in the pipeline`), поэтому `PATCH /term/{id}` молча не работает. Механика дословно: «for EACH term either promote it into the mined-delta file OR decline it in the mined-rejects file, then resume» (`backend/internal/pipeline/mining.go:58`=`func (r *Runner) runBankMiningStop` — ⚠ испр. 16.09: было :81,243; ⚠ фраза «the stop clears once every proposed term is promoted or rejected» ОПРОКИНУТА D39.144 — движок авто-продолжает с @@ -1505,8 +1505,8 @@ focus»), то есть цена платится на горячем пути ### G. Draft-only конвейер (PD-202) — ОТВЕЧАЮ: ДА, начерновленная глава «сделана»; правлю канон Проверено чтением обеих зон, и цена выше заявленной. У движка деплой без второго прохода — не -экзотика: разбиение выводится из РОЛЕЙ стадий (`pipeline/waverun.go:43-53`=`non-translator (editor/other) stage`), конфиг требует лишь -«хотя бы одна стадия» (`backend/internal/config/pipeline.go:953`=`at least one stage is required` — ⚠ испр. 17.09: было :926, ранее :841), ветка живая и покрыта тестом +экзотика: разбиение выводится из РОЛЕЙ стадий (`backend/internal/pipeline/waverun.go:43-53`=`non-translator (editor/other) stage`), конфиг требует лишь +«хотя бы одна стадия» (`backend/internal/config/pipeline.go:1035`=`at least one stage is required` — ⚠ испр. 17.09: было :926, ранее :841), ветка живая и покрыта тестом (`contractblockers_test.go:115-136`); в репозитории такого конфига пока нет, но выбирает его оператор, и платформа шаблон не читает. У платформы `units_done` — жёстко второй проход (`pgstore/sink.go:252-263`, `readmodel.go:200-208`). diff --git a/docs/architecture/15-money-path.md b/docs/architecture/15-money-path.md index d2f9e139..2a8463cf 100644 --- a/docs/architecture/15-money-path.md +++ b/docs/architecture/15-money-path.md @@ -23,7 +23,7 @@ - **Потолки — wiring, не семантика:** `Ceilings` намеренно исключены из `BriefHash` («Wiring fields (paths, ceilings, db) deliberately excluded», `backend/internal/config/book.go:345`=`Wiring fields (paths, ceilings, db) deliberately excluded`; канон — `backend/internal/config/book.go:369`=`canon := struct {`) ⇒ смена ДЕНЕЖНОГО потолка не двигает ни снапшот, ни ре-билл (D39.110 п.2б). ⚠ Верно ровно про ДЕНЬГИ: потолок СЕГМЕНТАЦИИ `edit_ceiling_out` в снапшот ВХОДИТ и меняет границы чанков (`backend/internal/pipeline/snapshot.go:34`=`EditCeilingOut int`). - **`status --json` отдаёт фигуры платформе:** `committed_usd`, `reserved_usd`, `book_ceiling_usd`, `ceiling_pct` = 100·(committed+reserved)/book_ceiling (`backend/internal/pipeline/status.go:342`=`book_ceiling_usd,omitempty`; арифметика `ceiling_pct` — `backend/internal/pipeline/status.go:949`=`100 * (committed + reserved)`). Дневной фигуры в status нет (PD-157 — `platform/docs/DEFECT_REGISTER.md`, греп `PD-157`). - **Леджер = НИЖНЯЯ граница; строка 78 снята с трекера 10.09, но её ЖИВОЙ ОСТАТОК несёт строка 377.** ⛔ **Испр. 10.09 — прежняя редакция этого пункта, написанная в тот же день, была НЕВЕРНА, и ошибка моя.** Она утверждала, будто оплаченный `2xx` с нечитаемым телом «прежде в `request_log` не попадал» и что это починил пак оборванных вызовов. Замер: ветвь `BilledDecodeError` писала свою строку с пометкой оценки ещё до пака — на `3f05fab^` это `stagerun.go:629`, введено коммитом `2f91b04` **24.07**, а пак этой ветви не касался вовсе (хитов `Estimated` в его диффе по `stagerun.go` — **0**). И носитель я процитировал ЧУЖОЙ: `backend/internal/pipeline/cutcall.go:133`=`rl := r.baseRequestLog(c.stage, c.chunk` обслуживает `*llm.AttemptCutError` (`backend/internal/pipeline/stagerun.go:745`=`var cut *llm.AttemptCutError` — ⚠ испр. 17.09: было :802, ранее :741), а речь шла о `*llm.BilledDecodeError` (`stagerun.go:709`) — это разные классы, и второй до `cutcall.go` не доходит. **Что пак действительно купил:** строку `request_log` за вызов, который оборвали МЫ (прежде он падал в ветвь «ничего не куплено» и получал $0-строку) — вот это `cutcall.go:133`. **Что остаётся правдой:** леджер — нижняя граница, потому что обрыв, за которым ретрай УСПЕЛ, исчезает бесследно (строка **377**). Живой замер `D39.86` — 3 вызова из 14, неизвестность $0.015111 при леджере $0.114378. -- **ГРАНИЦА ДЕНЕГ ЗА ОБОРВАННЫЙ НАМИ ВЫЗОВ — ФАКТ ДОСТАВКИ, а не статус-строка** (пак строки 360, акт `D39.232`; слово владельца — `D39.230`). Деньги берутся, только когда провайдер подтвердил запрос ответным объектом: `backend/internal/llm/attemptcut.go:221`=`Billable: answered && tr.afterHeaders()`. Наш собственный дедлайн даёт свой класс `attempt_timeout` без ретрая; дедлайн вычисляется ИЗ БЮДЖЕТА вызова, а не назначается (`backend/internal/llm/attemptcut.go:269`=`func (p RetryProfile) deriveDeadline`), так что эскалация, удвоившая `max_tokens`, удваивает и время. Оплаченный обрыв получает чекпойнт, и резюм его НЕ перепокупает. ⛔ Пометка «это оценка» — УСЛОВИЕ слова владельца (`D39.230` п.1) — сегодня в движке живёт ТРЕМЯ носителями (колонка `Estimated` в `request_log` — `backend/internal/store/requestlog.go:41`=`Estimated marks a row whose CostUSD`; пара `estimated_rows`/`estimated_usd` в `status --json` — `backend/internal/pipeline/status.go:911`=`Re-payment projection (spec`; печать оператору — `backend/cmd/tmctl/render.go:402`=`the degraded/err tail`), но НАРУЖУ не выходит ни одним каналом шва: строка **382**. ⚠ Формулировка испр. 10.09 — прежняя («живёт только в `status --json`») звала следующего чинить пометку, которая уже есть. +- **ГРАНИЦА ДЕНЕГ ЗА ОБОРВАННЫЙ НАМИ ВЫЗОВ — ФАКТ ДОСТАВКИ, а не статус-строка** (пак строки 360, акт `D39.232`; слово владельца — `D39.230`). Деньги берутся, только когда провайдер подтвердил запрос ответным объектом: `backend/internal/llm/attemptcut.go:221`=`Billable: answered && tr.afterHeaders()`. Наш собственный дедлайн даёт свой класс `attempt_timeout` без ретрая; дедлайн вычисляется ИЗ БЮДЖЕТА вызова, а не назначается (`backend/internal/llm/attemptcut.go:269`=`func (p RetryProfile) deriveDeadline`), так что эскалация, удвоившая `max_tokens`, удваивает и время. Оплаченный обрыв получает чекпойнт, и резюм его НЕ перепокупает. ⛔ Пометка «это оценка» — УСЛОВИЕ слова владельца (`D39.230` п.1) — сегодня в движке живёт ТРЕМЯ носителями (колонка `Estimated` в `request_log` — `backend/internal/store/requestlog.go:41`=`Estimated marks a row whose CostUSD`; пара `estimated_rows`/`estimated_usd` в `status --json` — `backend/internal/pipeline/status.go:911`=`Re-payment projection (spec`; печать оператору — `backend/cmd/tmctl/render.go:432`=`the degraded/err tail`), но НАРУЖУ не выходит ни одним каналом шва: строка **382**. ⚠ Формулировка испр. 10.09 — прежняя («живёт только в `status --json`») звала следующего чинить пометку, которая уже есть. - **Согласие на пере-оплату — ДВИЖКОВЫЙ гейт**. Прогон, который пере-покупает уже оплаченные юниты дороже порога, ОСТАНАВЛИВАЕТСЯ и требует явного согласия. Порог по умолчанию ратифицирован D20.2-Q2: `min($0.50, 5% × ProjectedBookUSD)`. Носитель — `backend/internal/pipeline/rebill.go`, греп `rebillConsentThreshold`; единственная ручка книги — `rebill_consent_usd` (валидатор `backend/internal/config/book.go`, греп `rebill_consent_usd`; 0 = ратифицированный дефолт). ⚠ Порог считается БЕЗ контура банк-ролей (D39.182 §3), то есть срабатывает позже, чем подсказывает полная проекция. diff --git a/docs/architecture/17-seam-inbound-law.md b/docs/architecture/17-seam-inbound-law.md index d6f1b280..ef8d071d 100644 --- a/docs/architecture/17-seam-inbound-law.md +++ b/docs/architecture/17-seam-inbound-law.md @@ -44,7 +44,7 @@ *Прецеденты:* потолок аргументом (D39.110 п.2б → `--ceiling-usd`, D39.122) · `--verify-bank` флагом (`platform/internal/runner/engine.go:116`=`func TranslateArgs(workdir string, verifyBank bool`) · дефолт `project_db` по конвенции каталога (`backend/internal/config/book.go:200`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")`) · конвенционное опциональное чтение `.auto-bank.yaml` - (`backend/internal/pipeline/mining.go:703`=`.auto-bank.yaml`). + (`backend/internal/pipeline/mining.go:734`=`.auto-bank.yaml`). 2. **Посреди прогона внутрь идёт ТОЛЬКО сигнал.** `SIGTERM` = «дочти чанк и отпусти лок» — ⚠ **это ратифицированная ЦЕЛЬ, а не сегодняшнее дерево (пометка 10.09).** Владелец ратифицировал две остановки (`D39.234` п.1а): первый сигнал — мягкий, он и означает «дочти и отпусти». В КОДЕ его ещё нет: `backend/cmd/tmctl/main.go:208`=`signal.NotifyContext(context.Background()` — ⚠ испр. 16.09: было :207 (пустая строка) отменяет контекст прогона, летящий вызов рвётся, книжится по оценке и метится `cancelled`. ⇒ норму НЕ переписывать под дерево, дерево догоняет норму; носитель работы — строка бэклога **381**, где этот адрес назван третьим. Всё остальное — на границах процессов. Арбитр — эксклюзивный flock проекта на всю жизнь прогона @@ -66,7 +66,7 @@ 4. **Незнакомый флаг = громкий отказ при спавне.** Сегодня это так по устройству разбора аргументов (`backend/cmd/tmctl/invocation.go:128`=`flag.NewFlagSet(cmd, flag.ContinueOnError)`); зафиксировать законом, а не полагаться на случайность. -5. **Глагол зовётся ТЕМ ЖЕ пиннутым бинарём, что и возобновляемая попытка** (`runs/spawn.go:250-258`=`engineBinary is the path an attempt is PINNED to`, +5. **Глагол зовётся ТЕМ ЖЕ пиннутым бинарём, что и возобновляемая попытка** (`platform/internal/runs/spawn.go:250-258`=`engineBinary is the path an attempt is PINNED to`, строка бэклога 139). Иначе файл, записанный новым бинарём, читается старым — а при нестрогом загрузчике сида (строка **212**) новое поле теряется МОЛЧА (⚠ **адрес испр. 10.09: строки 212 в живом трекере НЕТ** — она закрыта; факт оставлен как довод, номер снят, чтобы не звать читателя в пустоту). diff --git a/docs/experiments/00-provider-quirks.md b/docs/experiments/00-provider-quirks.md index 8289da45..11cf7430 100644 --- a/docs/experiments/00-provider-quirks.md +++ b/docs/experiments/00-provider-quirks.md @@ -86,7 +86,7 @@ ⛔⛔ **ВАХТА D39.92 ПЕРЕ-СНЯТА 2026-08-30 (полигон; `curl`, HTTP 200, страница 108 336 байт, sha256 `f28c4324…`) — ТАБЛИЦА У ВЕНДОРА ИЗМЕНИЛАСЬ, И ЗАПИСЬ НИЖЕ БОЛЬШЕ НЕ ВЕРНА.** Вендор исполнил обещанное сноской обновление маппинга. Живая редакция, дословно: *«(2) The mapping between the effort set by the user and the model's actual reasoning effort is as follows (**identical for `deepseek-v4-flash` and `deepseek-v4-pro`**)»* — и таблица теперь ОДНА на обе модели: `low→low · medium→high · high→high · xhigh→high · max→max`. Плюс *«(1) Thinking mode is enabled by default, with the default effort being `high`»*, а тумблер `{"thinking":{"type":"enabled/disabled"}}` подан как общий для модели DeepSeek, без оговорки «только flash». ⇒ **(i) на `deepseek-v4-pro` ручка `low` ТЕПЕРЬ РАБОТАЕТ и даёт `low`; (ii) рычаг бюджета размышления существует У ОБЕИХ моделей; (iii) размышление у `pro` в принципе ВЫКЛЮЧАЕМО тумблером.** ⚠ Это ДОКА, а не поведенческая проба: по норме проекта («слаг ≠ модель, при аномалии — поведенческая проба, не листинг») клейм действителен как ВЕНДОРСКОЕ ЗАЯВЛЕНИЕ и требует живой пробы перед денежным решением (смета — единицы центов). ⚠ И цена рычага уже замерена с другой стороны: `effort:"low"` **пере-вооружает эхо-мину** (п.4 ниже), а «reasoning-off + плотный CJK-вход = зона эха». ⚠ ЕЩЁ ОДИН ЖИВОЙ ФАКТ С ТОЙ ЖЕ СТРАНИЦЫ, которого у нас не записано нигде: *«Thinking mode does not support the `temperature`, `top_p`, `presence_penalty`, or `frequency_penalty` parameters… setting these parameters will not trigger an error but will also have no effect»* ⇒ **наш `temperature: 0.3`, который ростер шлёт на `deepseek-v4-pro`, МОЛЧА ИГНОРИРУЕТСЯ** — параметр в проводе есть, эффекта нет. ~~⇒ (i) **не слать `reasoning_effort` = ехать на `high`** — вот почему боевая форма упирается в стену (п.10); (ii) на `deepseek-v4-pro` ручка `low` НЕ РАБОТАЕТ (маппится в `high`); рычаг бюджета размышления существует ТОЛЬКО у flash~~ — **редакция до 30.08, оставлена под зачёркиванием: на ней стоят все денежные выводы фазы Д и вся её арифметика размышления. Дефолт `high` при не-присланной ручке — В СИЛЕ и в новой редакции** — **строка В СИЛЕ**: её отзыв от 05.08 сам ОТОЗВАН адверсариальным ревью того же дня (см. 3б), замер §3б признан недиагностичным; (iii) `none` в OpenAI-формате у DeepSeek не существует вовсе (колонка `reasoning.effort:"none"` — Anthropic-формат), запись 04.07 «`none` → 400» в силе. - **3г. ⭐ ПОВЕДЕНЧЕСКАЯ ПРОБА 2026-08-30 (полигон, фаза Д, $0.038276): ПАРАМЕТР `low` НА `pro` ТЕПЕРЬ ДОХОДИТ ДО МОДЕЛИ.** Дока 3а требовала живой пробы перед денежным решением — вот она. **Несущая улика — не размер размышления, а `prompt_tokens`:** при **побайтно одинаковых** `messages` (sha256 `51e7f427940cae15`, проверено исполнением) вызов без параметра дал **3270** входных токенов, вызов с `reasoning_effort:"low"` — **3191**, то есть **−79**. Тело вызова различалось РОВНО одним полем (`eval/dovodka/rol.py:668`=`def call_kwargs`), исходник главы приколот манифестом с гейтом остановки (`eval/role_topology/pair_en.py:132`=`def units(n_per_stratum: int | None = None`), шаблон промта не менялся с 20.08, кэш смещения не даёт (две клетки с `cached_tokens=0` дали остатки −31.8 и +10.4 при разбросе ±20–44). **ДВА контроля детерминизма.** (1) Замер 05.08 ниже: при побайтно одинаковых `messages` дефолт и `low` вернули ОДИНАКОВЫЕ 2124 — провайдер повторяет `prompt_tokens` в точности, когда параметр не действует; ⚠ но он снят ДО смены сервинга и на другом промте. (2) **Межэпохальный контроль — пред-полётные пробы того же рига:** один и тот же тривиальный запрос БЕЗ ручки дал `prompt_tokens` = **84 · 84 · 84 · 84**, причём четвёртая куплена 30.08 за две минуты до испытуемого вызова ⇒ **дефолтный счёт входа не сдвинулся ни на токен через смену вендорского сервинга**, и −79 принадлежит параметру, а не эпохе. ⚠ **Регулярность без объяснения:** 2203 − 2124 = **79** (05.08, `xhigh` над дефолтом) и 3270 − 3191 = **79** (30.08, дефолт над `low`) на совершенно разных промтах — похоже на серверную вставку фиксированного размера, но это догадка. ⇒ **Статус `low` на `pro` закрыт В ЧАСТИ «доходит ли»: ДОХОДИТ.** ⛔⛔ **НО ЭТО ПРО ПРОВОД ПОЛИГОННОГО РИГА, А НЕ ПРО ДВИЖОК, И РАЗНИЦА СТОИТ ЗАМЕРА.** Проверено исполнением 31.08: у `deepseek-v4-pro` в `backend/configs/models.yaml` блок `capabilities` несёт РОВНО `min_max_tokens` — **`reasoning`-capability отсутствует**, модель резолвится в `ReasoningNone`, и `reasoning: "off"` боевого `backend/configs/pipeline-c1.yaml` есть NO-OP (это и написано в самом конфиге, `pipeline-c1.yaml:69`=`ReasoningNone`). У `glm-5` capability есть (`control: extra_body_disable`), у `gemini-3.1-pro` — `mandatory`. ⛔⛔ **ПОПРАВКА 01.09, ТРЕТЬЯ РЕДАКЦИЯ ЭТОЙ СТРОКИ — ПРЕДЫДУЩАЯ («ДВИЖОК РУЧКУ НЕ ШЛЁТ ВООБЩЕ») БЫЛА НЕВЕРНА, И Я ЕЁ СНИМАЮ.** Я прочитал ОТСУТСТВИЕ `capabilities.reasoning` как «ручка не шлётся», не прочитав, ЧТО делает `ReasoningNone`. Дословно, `backend/internal/llm/capability.go:53`=`reasoning_effort`: **«ReasoningNone is the OpenAI-compat baseline: low|medium|high go out as reasoning_effort; off and "" emit nothing, leaving the provider's OWN default»**. И там же ниже: **«⚠ "Never turned off" is NOT "never configured" (D39.87): low|medium|high are a ratified way to size the thinking BUDGET on such a model»**. ⇒ **ДВИЖОК ШЛЁТ `low`/`medium`/`high` И ЭТО РАТИФИЦИРОВАННЫЙ СПОСОБ ЗАДАТЬ БЮДЖЕТ РАЗМЫШЛЕНИЯ.** Не шлёт он ровно два значения — `off` и пустое, и тогда едет вендор-дефолт `high`. **Именно `off` и стоит в боевом `pipeline-c1.yaml` у редактора** — поэтому редактор едет дефолтным `high`, но это ВЫБОР конфига, а не невозможность. ⇒ Ось «эффорт `off` против `low`» на движке ИЗМЕРИМА и требует правки ОДНОЙ строки конфига книги, без правки Go и без снятия защиты. Гейт `TestDeepSeekEchoMineRejected` запрещает ВЫКЛЮЧЕНИЕ размышления (`extra_body_disable`, `thinking` в `extra_body`, вложенный `chat_template_kwargs`, `effort`-capability) — `low` под запрет НЕ подпадает, потому что едет с размышлением ВКЛЮЧЁННЫМ. **Класс моей ошибки: отсутствие поля прочитано как отсутствие механизма, без чтения того, что делает дефолтная ветка.** Полигонный риг шлёт ручку своим проводом — это верно и остаётся, но движок ему в этом не уступает. Практические следствия: ⛔ **ЭТОТ АБЗАЦ СНЯТ 01.09 ЦЕЛИКОМ — он стоял на неверной посылке (см. поправку выше).** Он утверждал, что «любая движковая проба оси эффорта вернёт разницы нет» и что правка данных «уронила бы батарею». Оба ложны: движок шлёт `low`, а гейт, который я поминал, — `echo_mine_test.go:218` — лежит в `TestBoevoyConfigEditorDsproAndGrokReasoning` (⚠ я атрибутировал его `TestDeepSeekEchoMineRejected` — тоже ошибка) и требует `off` у редактора ТОЛЬКО в шиппинг-конфигах `pipeline-c1`/`c2`. Отдельный профиль книги с `reasoning: "low"` под него не подпадает. ⇒ **Ось «`off` против `low`» на движке измеряется отдельным конфигом книги — это ДАННЫЕ, ровно как записано в §14 отчёта холодного прогона; ни правки Go, ни снятия защиты не требуется.** ⚠ Класс ошибки, которого здесь чуть не случилось: **вендорский факт принят за свойство нашей системы** — между вендором и нами стоит реестр моделей, и он молчит. ⛔ **ВЕЛИЧИНА среза размышления ПО-ПРЕЖНЕМУ НЕ УСТАНОВЛЕНА:** наблюдённые ×2.52 (19 559 → 7 748 токенов) сняты БЛОКАМИ, разнесёнными на 9 дней. ⛔ **Несущий конфаунд здесь — НЕ дрейф, а сама смена сервинга, зафиксированная выше в 3а:** вендор переписал маппинг между двумя блоками, и сравнение «арм 21.08 против арма 30.08» приписывает ручке всё, что он поменял. Дрейф (строка в 3б) — второй и более слабый: у него есть ЗНАК (все шесть розыгрышей монотонно РАСТУТ), и при его продолжении ×2.52 было бы нижней границей среза. Чтобы величина стала известной, нужен интерливинг обоих армов в ОДНОМ блоке; пре-регистрация и цены — `docs/experiments/23-editor-tier.md` §Д46.3 и §Д47 (дорогая восьмёрка $0.565–0.620, дешёвая — $0.078–0.133). ⚠ **Эхо на `low` у `pro`: одна английская клетка дала кириллицу 1.000 и ноль латиницы — это НЕ снимает предупреждение «Не бесплатно» ниже**, эхо-мина живёт на плотном CJK, а проба была на английской паре. + **3г. ⭐ ПОВЕДЕНЧЕСКАЯ ПРОБА 2026-08-30 (полигон, фаза Д, $0.038276): ПАРАМЕТР `low` НА `pro` ТЕПЕРЬ ДОХОДИТ ДО МОДЕЛИ.** Дока 3а требовала живой пробы перед денежным решением — вот она. **Несущая улика — не размер размышления, а `prompt_tokens`:** при **побайтно одинаковых** `messages` (sha256 `51e7f427940cae15`, проверено исполнением) вызов без параметра дал **3270** входных токенов, вызов с `reasoning_effort:"low"` — **3191**, то есть **−79**. Тело вызова различалось РОВНО одним полем (`eval/dovodka/rol.py:668`=`def call_kwargs`), исходник главы приколот манифестом с гейтом остановки (`eval/role_topology/pair_en.py:132`=`def units(n_per_stratum: int | None = None`), шаблон промта не менялся с 20.08, кэш смещения не даёт (две клетки с `cached_tokens=0` дали остатки −31.8 и +10.4 при разбросе ±20–44). **ДВА контроля детерминизма.** (1) Замер 05.08 ниже: при побайтно одинаковых `messages` дефолт и `low` вернули ОДИНАКОВЫЕ 2124 — провайдер повторяет `prompt_tokens` в точности, когда параметр не действует; ⚠ но он снят ДО смены сервинга и на другом промте. (2) **Межэпохальный контроль — пред-полётные пробы того же рига:** один и тот же тривиальный запрос БЕЗ ручки дал `prompt_tokens` = **84 · 84 · 84 · 84**, причём четвёртая куплена 30.08 за две минуты до испытуемого вызова ⇒ **дефолтный счёт входа не сдвинулся ни на токен через смену вендорского сервинга**, и −79 принадлежит параметру, а не эпохе. ⚠ **Регулярность без объяснения:** 2203 − 2124 = **79** (05.08, `xhigh` над дефолтом) и 3270 − 3191 = **79** (30.08, дефолт над `low`) на совершенно разных промтах — похоже на серверную вставку фиксированного размера, но это догадка. ⇒ **Статус `low` на `pro` закрыт В ЧАСТИ «доходит ли»: ДОХОДИТ.** ⛔⛔ **НО ЭТО ПРО ПРОВОД ПОЛИГОННОГО РИГА, А НЕ ПРО ДВИЖОК, И РАЗНИЦА СТОИТ ЗАМЕРА.** Проверено исполнением 31.08: у `deepseek-v4-pro` в `backend/configs/models.yaml` блок `capabilities` несёт РОВНО `min_max_tokens` — **`reasoning`-capability отсутствует**, модель резолвится в `ReasoningNone`, и `reasoning: "off"` боевого `backend/configs/pipeline-c1.yaml` есть NO-OP (это и написано в самом конфиге, `backend/configs/pipeline-c1.yaml:120`=`NO-OP на deepseek-v4-pro (ReasoningNone)`). У `glm-5` capability есть (`control: extra_body_disable`), у `gemini-3.1-pro` — `mandatory`. ⛔⛔ **ПОПРАВКА 01.09, ТРЕТЬЯ РЕДАКЦИЯ ЭТОЙ СТРОКИ — ПРЕДЫДУЩАЯ («ДВИЖОК РУЧКУ НЕ ШЛЁТ ВООБЩЕ») БЫЛА НЕВЕРНА, И Я ЕЁ СНИМАЮ.** Я прочитал ОТСУТСТВИЕ `capabilities.reasoning` как «ручка не шлётся», не прочитав, ЧТО делает `ReasoningNone`. Дословно, `backend/internal/llm/capability.go:53`=`reasoning_effort`: **«ReasoningNone is the OpenAI-compat baseline: low|medium|high go out as reasoning_effort; off and "" emit nothing, leaving the provider's OWN default»**. И там же ниже: **«⚠ "Never turned off" is NOT "never configured" (D39.87): low|medium|high are a ratified way to size the thinking BUDGET on such a model»**. ⇒ **ДВИЖОК ШЛЁТ `low`/`medium`/`high` И ЭТО РАТИФИЦИРОВАННЫЙ СПОСОБ ЗАДАТЬ БЮДЖЕТ РАЗМЫШЛЕНИЯ.** Не шлёт он ровно два значения — `off` и пустое, и тогда едет вендор-дефолт `high`. **Именно `off` и стоит в боевом `pipeline-c1.yaml` у редактора** — поэтому редактор едет дефолтным `high`, но это ВЫБОР конфига, а не невозможность. ⇒ Ось «эффорт `off` против `low`» на движке ИЗМЕРИМА и требует правки ОДНОЙ строки конфига книги, без правки Go и без снятия защиты. Гейт `TestDeepSeekEchoMineRejected` запрещает ВЫКЛЮЧЕНИЕ размышления (`extra_body_disable`, `thinking` в `extra_body`, вложенный `chat_template_kwargs`, `effort`-capability) — `low` под запрет НЕ подпадает, потому что едет с размышлением ВКЛЮЧЁННЫМ. **Класс моей ошибки: отсутствие поля прочитано как отсутствие механизма, без чтения того, что делает дефолтная ветка.** Полигонный риг шлёт ручку своим проводом — это верно и остаётся, но движок ему в этом не уступает. Практические следствия: ⛔ **ЭТОТ АБЗАЦ СНЯТ 01.09 ЦЕЛИКОМ — он стоял на неверной посылке (см. поправку выше).** Он утверждал, что «любая движковая проба оси эффорта вернёт разницы нет» и что правка данных «уронила бы батарею». Оба ложны: движок шлёт `low`, а гейт, который я поминал, — `echo_mine_test.go:218` — лежит в `TestBoevoyConfigEditorDsproAndGrokReasoning` (⚠ я атрибутировал его `TestDeepSeekEchoMineRejected` — тоже ошибка) и требует `off` у редактора ТОЛЬКО в шиппинг-конфигах `pipeline-c1`/`c2`. Отдельный профиль книги с `reasoning: "low"` под него не подпадает. ⇒ **Ось «`off` против `low`» на движке измеряется отдельным конфигом книги — это ДАННЫЕ, ровно как записано в §14 отчёта холодного прогона; ни правки Go, ни снятия защиты не требуется.** ⚠ Класс ошибки, которого здесь чуть не случилось: **вендорский факт принят за свойство нашей системы** — между вендором и нами стоит реестр моделей, и он молчит. ⛔ **ВЕЛИЧИНА среза размышления ПО-ПРЕЖНЕМУ НЕ УСТАНОВЛЕНА:** наблюдённые ×2.52 (19 559 → 7 748 токенов) сняты БЛОКАМИ, разнесёнными на 9 дней. ⛔ **Несущий конфаунд здесь — НЕ дрейф, а сама смена сервинга, зафиксированная выше в 3а:** вендор переписал маппинг между двумя блоками, и сравнение «арм 21.08 против арма 30.08» приписывает ручке всё, что он поменял. Дрейф (строка в 3б) — второй и более слабый: у него есть ЗНАК (все шесть розыгрышей монотонно РАСТУТ), и при его продолжении ×2.52 было бы нижней границей среза. Чтобы величина стала известной, нужен интерливинг обоих армов в ОДНОМ блоке; пре-регистрация и цены — `docs/experiments/23-editor-tier.md` §Д46.3 и §Д47 (дорогая восьмёрка $0.565–0.620, дешёвая — $0.078–0.133). ⚠ **Эхо на `low` у `pro`: одна английская клетка дала кириллицу 1.000 и ноль латиницы — это НЕ снимает предупреждение «Не бесплатно» ниже**, эхо-мина живёт на плотном CJK, а проба была на английской паре. **3д. ⚠ 30.08 ($0.134804, интерливинг Д47): ДВА ОДИНОЧНЫХ РОЗЫГРЫША ДЕФОЛТНОГО ПУТИ `deepseek-v4-pro` РАЗОШЛИСЬ В 38 РАЗ. ⛔ ПРИЧИНА НЕ УСТАНОВЛЕНА — «модель сдвинулась» и «выпал хвост» ОБЕ ЖИВЫ.** (Первая редакция этой записи подавала ×38 как замеренный сдвиг во времени; поправлено в тот же день — по лучшей имеющейся оценке разброса это ≈3.1 sd, редкое событие, но не невозможное. Оценка разброса: sd логарифма 0.82, то есть одно sd = ×2.3, снята по шести группам «тот же тег, разные розыгрыши», 18 вызовов, СУДЕЙСКАЯ роль — на переводе чистых повторов в корпусе нет. Разбор — `docs/experiments/23-editor-tier.md` §Д47.7.) Побайтно один промт (sha256 сверен), одна и та же глава, `reasoning_effort` НЕ слался ни разу: 21.08 модель думала **466** токенов, 30.08 — **17 757**. Цена той же клетки $0.005034 → $0.076893. **Для практики обе гипотезы дают одно и то же предупреждение: любая смета DeepSeek, снятая с ОДИНОЧНОГО прошлого вызова, может ошибиться на порядок, и денежные планы на неё ставить нельзя.** ⭐ **Следствие, которое дороже самой записи: сметы и сравнения моделей по размышлению нужно строить на ПОВТОРАХ.** Считано: при sd логарифма 0.82 парный дизайн ловит эффект ×2.5 за 13 пар, ×2.0 за 22, ×1.4 за 93 (80% мощности, α=0.05). Проверено на себе: пре-регистрация оценила восемь пар в $0.078–0.133, одна пара стоила $0.1348. ⭐ **Зато счёт ВХОДА через тот же разрыв стабилен до токена:** `prompt_tokens` = 3 256 и 21.08, и 30.08 при не-присланной ручке, а `low` сдвигает его ровно на −79 (3 177) — то есть провенанс параметра читается по входу даже там, где выход уехал ×38. ⚠ Наблюдённый ВНУТРИ ОДНОГО БЛОКА эффект ручки — ×1.40 (17 757 → 12 676), заметно меньше ×2.52, полученных сравнением через девять дней: конфаунд эффект РАЗДУВАЛ. n=1, знаковый тест p=1.0 ⇒ величина по-прежнему **НЕ УСТАНОВЛЕНА**; разбор — `docs/experiments/23-editor-tier.md` §Д47.6. @@ -168,7 +168,7 @@ - ⛔⛔ **Gemini, ДВА СИСТЕМНЫХ СООБЩЕНИЯ = ПРОМТ ТЕРЯЕТСЯ МОЛЧА (полигон 22.08, живая проба `eval/dovodka/proba_gemini.py`).** **СТРУКТУРНЫЙ ФАКТ ВЕНДОРА** (`ai.google.dev/api/generate-content`, страница помечена 2026-08-17, снято 2026-08-28): в нативном `GenerateContentRequest` поле `systemInstruction` — ОДИН `object (Content)` (в той же таблице `contents[]` и `tools[]` несут суффикс повторяемого поля), а `Content.role` документирован как «Must be either 'user' or 'model'» ⇒ второму системному ХОДУ в нативном запросе места нет. ⚠ **ПОПРАВКИ ПИНГА №19 (28.08, D39.164), внесены 29.08 — прежняя редакция была сильнее источника в трёх местах.** (а) ~~«слой ФИЗИЧЕСКИ не может пронести больше одного системного»~~ — это про НАТИВНЫЙ запрос; **что делает с двумя системными OpenAI-совместимый ШИМ, вендор не документирует НИГДЕ** (раздел «Current limitations» молчит и в живой странице, и в снимке Wayback 2026-08-21). (б) ~~«уход на нативный API проблемы НЕ решает»~~ верен по сути, но НЕ по причине: `Content.parts[]` — ПОВТОРЯЕМОЕ поле, и вендорская заметка на `systemInstruction` гласит «Only text should be used in parts and content in each part will be in a separate paragraph» ⇒ нативный API МОЖЕТ нести N системных текстов как N частей ОДНОГО `Content`; склеивать всё равно пришлось бы, но «физически не может» — неправда. (в) форум Google — **НЕ вендорский источник**: тред `discuss.ai.google.dev/t/86097` (30.05.2025) — два поста, оба от НЕ-сотрудников, ответа Google нет; ссылаться на него наравне с замером нельзя. При двух и более системных лишние выбрасываются без ошибки. ⛔ **УСТАНОВЛЕНО 29.08: ВЫЖИВАЕТ ПОСЛЕДНЕЕ СИСТЕМНОЕ, ВЫБРАСЫВАЕТСЯ ПЕРВОЕ** (полигон, Д35.3 отчёта 23; $0, детерминированно, на 16 уже купленных клетках). Доказательство арифметическое: у арма `RE` первое системное (ролевой промт) **побайтно одинаково на всех 16 главах**, второе (глоссарий) — **разное** (255–404 знака, термины из текста главы); разность `RE.prompt_tokens − RQ.prompt_tokens` равна **2436 РОВНО на каждой из 16 глав, sd = 0.00** ⇒ выброшено сообщение ПОСТОЯННОГО размера, а таково только первое; будь выброшены оба, разность росла бы вместе с глоссарием. Побочно: `prompt_tokens` карантинных клеток коррелируют с длиной глоссария +0.52 и с длиной исходника +0.04 — глоссарий внутри них физически есть. ~~⚠ КАКОЕ именно выживает — НЕ УСТАНОВЛЕНО~~ — вопрос ЗАКРЫТ этим замером; прежняя интерпретация «не выжило НИ ОДНО» неверна: она сравнила 535 токенов с «размером одного user-сообщения» НА ГЛАЗ, а при шкале 0.324 ток/знак русского глоссарий в 300 знаков весит ~100 токенов и в такой оценке теряется. ⚠ Форум Google (`discuss.ai.google.dev/t/86097`) говорит то же самое, но он **не источник** (не-сотрудники, ответа Google нет) — здесь он лишь СОВПАДАЕТ с замером, а не подпирает его. ⚠⚠ **ГРАНИЦА КЛЕЙМА, названная пингом №19 и подтверждённая здесь:** замер снят на `gemini-3.1-flash-lite` и на клетках, купленных 21.08.2026. Слой у `gemini-3.1-pro-preview` (тот, что стоит в цепочке эскалации) ТОТ ЖЕ, но слаг другой — клейм переносится на него как ПРАВДОПОДОБНЫЙ, не как замеренный. Для решения это безразлично: движок склеивает системные сам. Документированного вендором ограничения в разделе «Current limitations» НЕТ. Замер на одном и том же системном тексте в 7970 знаков: ОДНИМ сообщением → `prompt_tokens` 2994 и маркерная инструкция ИСПОЛНЕНА; ДВУМЯ → **535 токенов** ~~(это размер одного user-сообщения)~~ **и инструкция НЕ исполнена** — ⚠ оценка «размер одного user-сообщения» СНЯТА: 535 = user + глоссарий, см. закрытие вопроса выше. Ответ приходит с `finish=stop` и выглядит правильным переводом — отказа нет, есть тихая потеря инструкции, невидимая всем гейтам годности. ⚠ Так были куплены 16 клеток арма `RE` (промт-роль на gemini): ~~они переведены ВООБЩЕ БЕЗ ИНСТРУКЦИИ~~ — **уточнено 29.08 тем же замером, что закрыл вопрос выше: они переведены БЕЗ РОЛЕВОГО ПРОМТА, но С ГЛОССАРИЕМ** (до модели доехали ~130 токенов канонных форм из 2 566). Вердикт о переносимости из них был бы ложным в обоих чтениях; но арм, собранный из этих клеток, называется «без ролевого промта», а не «без инструкции» — иначе класс «банк» читается неверно. Лечение: склеивать системные в одно ДЛЯ ЭТОГО ВЕНДОРА (`rol.ONE_SYSTEM`/`fit_msgs`) — общая склейка сменила бы форму запроса у всех моделей и рассогласовала бы уже купленные клетки. ⚠ Проверено, что у `deepseek-v4-pro`, `gpt-5.6-luna`, `glm-5` и `grok-4.3` два системных доезжают целиком. - ⛔⛔ **ТОТ ЖЕ ДЕФЕКТ ЖИВЁТ В ДВИЖКЕ, А НЕ ТОЛЬКО В ПОЛИГОНЕ (аудит 22.08).** `backend/internal/pipeline/render.go:318-320`=`Content: injection` штатно строит ВТОРОЕ системное сообщение — инъекцию банка памяти (глоссарий/STM), тогдашняя редакция `toOpenAIMessages` копировала сообщения дословно (строки 517–522 на 22.08 — ⚠ адрес ИСТОРИЧЕСКИЙ, функция ПЕРЕСТРОЕНА 28.08, живой адрес в ✅ ниже), склейки под Gemini не было нигде, а `gemini-3.1-pro-preview` стоит в ратифицированной цепочке эскалации во ВСЕХ арм-конфигах (`pipeline-c1.yaml:129`, `pipeline-arm-glm.yaml:61`, `pipeline-arm-deepseek-pro.yaml:62`, `pipeline-arm-mistral.yaml:61`). ⇒ **любой эскалационный хоп на Gemini при непустой инъекции ТИХО терял текст.** Оговорка D22.3 «экспозиция Ф1 нулевая» держалась только на том, что до Gemini в дефолте не доходят. ✅ **ДЕФЕКТ ДВИЖКА ЗАКРЫТ 28.08 лендингом `7d0c6f2`** — проверено кодом 29.08, а не пересказано: у провайдера `gemini` объявлено `system_messages: single` (`backend/configs/models.yaml:183`=`system_messages: single`), адаптер склеивает ВЕДУЩИЙ system-ран в одно сообщение и падает громко на системном ходе после не-системного (`backend/internal/llm/httpllm.go:766`=`func toOpenAIMessages`), инвариант запинен тестом (`backend/internal/llm/systemmessages_test.go:78`=`TestSystemMessagesSingleCarriesTheInjection`). ⇒ строки выше про движок — ИСТОРИЯ, а не открытый долг. - ⛔ **НО ЦЕНА ДЕФЕКТА ДО ПОЧИНКИ ОЦЕНИВАЛАСЬ НЕВЕРНО, и это правится здесь (полигон 29.08, Д35.3).** По установленному выше правилу выживает ПОСЛЕДНЕЕ системное, а `render.go:247-258`=`Role: "system"` кладёт первым **сам промт стадии**, вторым — инъекцию банка. ⇒ до 28.08 хоп в Gemini терял **не глоссарий, а ВЕСЬ ПРОМТ СТАДИИ**, оставляя глоссарий: модель получала список канонных форм и текст — без единого слова о том, что с ними делать. Ровно так выглядят 16 клеток `RE` полигона: перевод есть, `finish=stop`, русский, правильной длины, инструкции за ним нет. **Тихая потеря промта не отличима от нормального ответа ничем, кроме счётчика входных токенов.** ⇒ решение «склеивать самим» было единственно верным и при неизвестном тогда чтении квирка; а любой ИСТОРИЧЕСКИЙ вывод, снятый на Gemini-хопе до 28.08, надо читать как «стадия работала без своего промта». ⚠ Класс в проекте уже был решён рядом: `backend/internal/llm/provider_anthropic.go:169-179`=`wire.System = append` сворачивает ведущий system-префикс в одно поле и падает громко на system посреди диалога — просто не обобщён на Gemini. **Зона бэкенда, пинг оркестратору отправлен.** + ⛔ **НО ЦЕНА ДЕФЕКТА ДО ПОЧИНКИ ОЦЕНИВАЛАСЬ НЕВЕРНО, и это правится здесь (полигон 29.08, Д35.3).** По установленному выше правилу выживает ПОСЛЕДНЕЕ системное, а `backend/internal/pipeline/render.go:316-318`=`Role: "system"` кладёт первым **сам промт стадии**, вторым — инъекцию банка. ⇒ до 28.08 хоп в Gemini терял **не глоссарий, а ВЕСЬ ПРОМТ СТАДИИ**, оставляя глоссарий: модель получала список канонных форм и текст — без единого слова о том, что с ними делать. Ровно так выглядят 16 клеток `RE` полигона: перевод есть, `finish=stop`, русский, правильной длины, инструкции за ним нет. **Тихая потеря промта не отличима от нормального ответа ничем, кроме счётчика входных токенов.** ⇒ решение «склеивать самим» было единственно верным и при неизвестном тогда чтении квирка; а любой ИСТОРИЧЕСКИЙ вывод, снятый на Gemini-хопе до 28.08, надо читать как «стадия работала без своего промта». ⚠ Класс в проекте уже был решён рядом: `backend/internal/llm/provider_anthropic.go:169-179`=`wire.System = append` сворачивает ведущий system-префикс в одно поле и падает громко на system посреди диалога — просто не обобщён на Gemini. **Зона бэкенда, пинг оркестратору отправлен.** - **Gemini**: `safety_settings` через OpenAI-совместимый слой **не передаются** (HTTP 400 «Unknown name safety_settings»). Дефолт фильтров у 2.5/3 — OFF (gap-2). Для явного контроля фильтров (роль судьи 18+) нужен **нативный** Gemini API, не OpenAI-слой. ⚠ **D22.6: на ГРАФИЧНОЙ эротике Gemini 3.x fail-closed `PROHIBITED_CONTENT` (8× wire-verified, exp11) — фильтр НЕконфигурируем, нативный API не спасает; судья эротики — только Grok. На violence/SFW Gemini-судья жив.** `finish_reason` приходит СОСТАВНОЙ строкой `'content_filter: PROHIBITED_CONTENT'` (точные матчеры мертвы — D22.8а). Биллинг Gemini: thinking ТОЛЬКО в `total_tokens` → `reasoning: additive_total`, иначе недоучёт 146×/вызов (D22.3). Нативное имя поля — `usageMetadata.thoughtsTokenCount`; слой AI-Studio `completion_tokens_details.reasoning_tokens` не отдаёт ВОВСЕ, тогда как Vertex AI то же поле отдаёт (форум 15.01.2026) — при переезде на Vertex учёт менять. ⚠⚠ **`reasoning_tokens=0` у Gemini — НОРМА, а не измерение: вывод «размышление выключено» из этого поля НЕДЕЙСТВИТЕЛЕН.** Полигон 22.08 на этом и поскользнулся, объявив «вендор-дефолт flash-lite = размышление ВЫКЛ (замерено)»; считать надо `total − prompt − completion`. ⚠ **В роли редактора Gemini течёт сервис-преамбулой в выход («Вот отредактированный…», 6/6 чанков — exp12) → за output-санитайзером D30.3.** ⚠ **Дополнение эксп-21 (08.08):** fail-closed ответ OpenAI-слоя приходит вовсе БЕЗ объекта `message` (не с пустым `content`) — парсер обязан переживать его отсутствие; на вебновелльном violence-материале срабатывания массовые (10 клеток судейского прогона с `content_filter: PROHIBITED_CONTENT`) ⇒ оговорка «на violence/SFW Gemini-судья жив» целиком НЕ держится — Gemini непригоден как ЕДИНСТВЕННЫЙ второй контур на этом материале (эксп-21 §1/§8). - **Qwen Model Studio**: внешний фильтр `data_inspection_failed` на вход/выход, неотключаем (риск молчаливых вырезаний) — не тестировано (нет ключа). - Детект отказов/вырезаний — см. `eval/refusal_bench.py` (паттерны soft-отказа + coverage-гейт по len_ratio/sent_cov, пороги в эксп. 02). diff --git a/docs/experiments/23-editor-tier.md b/docs/experiments/23-editor-tier.md index ed0d8935..cf84e36b 100644 --- a/docs/experiments/23-editor-tier.md +++ b/docs/experiments/23-editor-tier.md @@ -5271,9 +5271,9 @@ user-сообщения» на глаз. 2 436 токенов на 7 525 зна ✅ **ДЕФЕКТ ДВИЖКА УЖЕ ЗАКРЫТ, И Я ЭТО ПРОВЕРИЛ КОДОМ, А НЕ ПРЕДПОЛОЖИЛ.** Лендинг `7d0c6f2` (28.08) ввёл ось `Capability.SystemMessages`: у провайдера `gemini` стоит -`backend/configs/models.yaml:137`=`system_messages: single`, адаптер склеивает ВЕДУЩИЙ system-ран в +`backend/configs/models.yaml:183`=`system_messages: single`, адаптер склеивает ВЕДУЩИЙ system-ран в одно сообщение и громко падает на системном ходе после не-системного -(`backend/internal/llm/httpllm.go:528`=`func toOpenAIMessages`), инвариант запинен тестом +(`backend/internal/llm/httpllm.go:766`=`func toOpenAIMessages`), инвариант запинен тестом (`backend/internal/llm/systemmessages_test.go:78`=`TestSystemMessagesSingleCarriesTheInjection`). ⚠ Первая редакция ЭТОГО ЖЕ абзаца, написанная час назад, объявляла дефект живым и слала пинг в бэкенд — она стояла на строке `00-provider-quirks.md:158`, устаревшей на один день. @@ -7786,7 +7786,7 @@ $0.040093 — расхождение 0.0%). Значит прайс-таблиц **Что остаётся верным после правки:** редакторская стадия покупает **беглость и термин**; ось верности она не двигает; и **прибора этой оси у нас по-прежнему нет** — панель объявлена слепой к -ней (`23-editor-tier.md:6056`=`оси ВЕРНОСТИ владельца`). Утверждение «мы измеряем половину цели» +ней (`docs/experiments/23-editor-tier.md:6057`=`оси ВЕРНОСТИ владельца`). Утверждение «мы измеряем половину цели» устояло; утверждение «за вторую половину мы платим» — снято. **Класс моей ошибки:** «не покупает» прочитано как «платит» — усиление вывода в сторону тревоги. @@ -8465,6 +8465,6 @@ sd(log R̂(low₂) − log R̂(low₁)) = 1.493 по n=19 ⇒ σ = 1.493/√ ⛔ **И существеннее адресов: претензия тела «носитель решения врёт прямо сейчас» (про арм-конфиги) ЗАКРЫТА — носители ИСПРАВЛЕНЫ.** Обоснования «×2 дешевле glm» в дереве больше нет ни в одном из двух файлов; на его месте пере-пин цен `D39.137` п.4: -`backend/configs/pipeline-c1.yaml:97`=`ПЕРЕВЁРНУТА пере-пином цен` и `backend/configs/pipeline-arm-glm.yaml:6`=`ПЕРЕВЁРНУТА пере-пином цен` +`backend/configs/pipeline-c1.yaml:99`=`ПЕРЕВЁРНУТА пере-пином цен` и `backend/configs/pipeline-arm-glm.yaml:6`=`ПЕРЕВЁРНУТА пере-пином цен` — «в ПИК dspro дороже glm ×1.26, офф-пик дешевле ×1.59». Контроль: «дешевле» встречается в каждом файле РОВНО ОДИН раз, и оба раза — про пере-пин; «×2» — ноль вхождений в обоих. ⇒ читающий тело НЕ должен заводить по этой претензии работу. diff --git a/docs/orchestrator-norms-scratchpad.md b/docs/orchestrator-norms-scratchpad.md index c48607b2..6f04d5de 100644 --- a/docs/orchestrator-norms-scratchpad.md +++ b/docs/orchestrator-norms-scratchpad.md @@ -118,7 +118,7 @@ UNKNOWN 1 — её, дошёл один раз до правок; десять | 12 | — (вне кодовых зон) | КОНСИЛИУМ «банк памяти длинной книги — как строить»: наследует банковую работу смены после отзыва проекта оркестратора; совет агентов, Fable 1–2 | textmachine-main-1e | ✅ своими словами; три опасности: шесть открытых решений против лимита в пять вопросов · исход «не термин» против закрытия D39.102 п.2 · угол «журнал с окнами» против почти пустых окон — все по делу, ответы отправлены | ✅ **ПРИНЯТ актом `D39.253`** 16.09: сдан по правилу остановки, приёмка — пять осей вне консилиума (50 находок, 14 мажоров), затем круг дофикса и узкий закрывающий. ⭐ Совет нашёл ратифицированный ответ на заглавный кейс владельца (D5 п.1, D7 правка 1), мимо которого прошли оба отозванных проекта. ⚠ Мои ошибки смены — в п.6 акта. Выдан 16.09 после механической сверки 13 блоков и ЧЕТЫРЁХ кругов опровергателя: 4 мажора + 15 миноров → 1 + 7 → 1 + 2 → 0 + 2, миноры последнего внесены. ⭐ Опровергатель нашёл то, мимо чего прошли оба отозванных проекта: ратифицированный ответ на заглавный кейс владельца (D5 п.1, D7 правка 1) и что у движковых строк банка окон почти нет (`since_ch` > 0 у 2 строк на книгу, `until_ch` = 0 у всех). ⚠ Дважды мажор родила МОЯ правка: цитата о заморозке без её предмета · баннер выписки, объявивший опровергнутым верное утверждение | | 13 | бэкенд | БАНК ЗА $0 — фундамент «умного участка»: ряды 441 · 445 · 449 · 459 | — (не выдан) | — | ⏳ **промт написан 16.09**, прошёл круг опровергателя фабла (7 мажоров: 5 внесены, **M1 опровергнут моим замером** — опровергатель напечатал «баз нет», а их 19, включая `bank_stop_presented` на 67 строк; 1 уточнён по ссылке). ✅ **ПРИНЯТ актом `D39.257`** 16.09: пять приборов (батарея 24/24 · мой прогон её 37 мутаций 37/37 без осечек · три оси вне сдачи · числа пере-сняты третьим прибором парсер-свободно · деньги 0 платных путей при контроле 192 файла). Два мажора названы, остаток принят. ⚠ Моя ошибка смены: снёс её рабочий корень мутаций — норма записана выше. ⚠ В самой выдаче явно попрошено ВЕРНУТЬ заказ, если сессия не свежая: норма «новый пак — новой сессии» стоит на том, что карта чтения писана для свежего читателя, а занятость чужого контекста снаружи не видна | -| 14 | бэкенд | РЕШЁННОЕ НЕ ПЕРЕ-СПРАШИВАЕТСЯ: базис решённого как ИСТОЧНИК ОТВЕТОВ (ряд 130) + страж разрыва окон (ряд 490) | textmachine-main-57 | ✅ **своими словами, десять пунктов, механика а не текст** — и ДО начала работы названы два кандидата на отказ с замером (ряд 447 как смена продуктового поведения · сторож растущей книги, чья посылка вынесена владельцу и ответа не имеет). Ошибок понимания в эхо нет: воспроизведены порядок блоков, инвариант «базис — ИСТОЧНИК ответа, не фильтр», один писатель на двух границах, инвариантность отпечатка к способу отбора при пар-данной ширине, обязательность статуса `draft` и развилка юнитов. ⛔ **РЕЛЕЙ 17.09, через считанные минуты после выдачи: база прогона A в промте была названа НЕ ТА, и сессия уже строила прибор.** Я вписал `evidence/v16-readonly/project.db` — замер: `book_id` `guzhenren-coldrun-v16`, глоссарий 53, Σ`cost_usd` 0.43611; платный A — `stand/books/bk_ROEHZBD46ALFI43E/project.db`, глоссарий **69** (число всех банковых замеров), Σ **0.419423**, сходится с ратифицированной суммой. Нашёл советчик, пере-снял я. Той же правкой доехали три ловушки материала: все `book.yaml` прогонов ссылаются на несуществующий `/home/ubuntu-26/…` (загрузка либо падает, либо ТИХО берёт конфиги головы) · вход контура `mining-contrast.zh.txt` в git отсутствует вовсе и живёт только в зеркалах прогонов · базы в WAL-режиме, копировать с `-wal`/`-shm`. ⚠ Урок мой: **материал замера я назвал по имени каталога, а не по замеру содержимого** — «evidence» и «readonly» звучали как платная улика, и этого хватило, чтобы не открыть базу. Заказ, отправляющий мерить не тот предмет, дороже битого якоря: якорь виден линтеру, неверная база — никому. ⏳ **ДОЛГ НА ЛЕНДИНГ ЭТОГО ПАКА, записан чтобы не растворился:** ⚠ **ЧИСЛО ПЕРЕ-СНЯТО ВЕЧЕРОМ: не два якоря, а ДВАДЦАТЬ ПЯТЬ, и это не долг, а движущаяся цель.** Пак 57 переписывает восемнадцать путей движка, `terminologist.go` вырос 1473 → 1549 строк, и разбор трёх состояний даёт: **22 ПЕРЕЕХАЛИ** (токен в файле есть, уехал номер — напр. `attachClassifiedGender stamps` с :1306 на :1382, `paidAfterBurns` с :899 на :975), **2 транзиентных** (жив в срезе, нет в дереве), **0 исчезнувших совсем**. ⛔ Первый мой счёт различал только два состояния и отправил 22 позиции в «неизмеренное», которое я чуть не прочитал как «всё хорошо»: **прибор, у которого корзин меньше, чем состояний у предмета, печатает правдоподобный итог и молчит о разнице.** ⇒ после лендинга пака 57 пере-нацелить ВСЕ позиции одной правкой, считая по трём состояниям, а не по двум. Прежняя редакция записи (ниже) называла два якоря — она верна по механизму и занижена по числу. Механизм: правка зоны в `backend/internal/lang/langpack.go` сдвинула файл на 12 строк, и первые ДВА якоря стали транзиентно красны — `docs/BACKEND_SETTLED_STAYS_SETTLED_SESSION_PROMPT.md` → `langpack.go:744`=`kwic_per_term` (живёт на `:756`) и `docs/BACKLOG.md:277`=`manifest declares no channel` (ряд 347) → та же цель `langpack.go:632`, живущая теперь на `:644`. Замер, различающий транзиент от долга: оба токена ЦЕЛЫ в срезе `HEAD` на прежних строках и мертвы только в дереве ⇒ это чужой незакоммиченный WIP в цели, о котором прибор предупреждает сам, а не моя гниль. ⛔ Чинить ДО лендинга нельзя — цель движется, и пере-нацеливание сломается при следующей её правке или при откате. ⚠ Но у второго якоря есть и НЕ транзиентная половина: путь написан не от корня (`lang/langpack.go`), и это дефект формы, переживший сегодняшнюю чистку, потому что мой шаблон искал только полные пути. ⇒ после лендинга ОДНОЙ правкой: развернуть путь и пере-нацелить оба якоря по найденным, а не вычисленным адресам | ⏳ **ВЫДАН 17.09** после опроса кандидатов (свежих нашлось ДВЕ — `57` и `78`; взята `57`, второй сказано не занимать зону). Оба рубежа пройдены: механическая сверка 13 блоков (27 якорей, все живы, токен по адресу, коротких нет) и субагент-опровергатель — **12 находок, 9 денежных, все внесены**. ⭐ Чем окупился опровергатель: (1) я вписал «текста в коде УЖЕ НЕТ, 0 совпадений» о строке, которая ЖИВА ДОСЛОВНО, — грепал по одному файлу, увидел начало длинной строки, обрезанной по ширине, и прочёл обрезок как ФАКТ; у неё вдобавок третий носитель, голден операторских сообщений, которого я не видел вовсе; (2) замер M1 был заказан на копиях купленных баз, а таблицы чанков в них НЕТ (17 и 16 таблиц) — предмет из них не строится, нужен исходник отдельным файлом; (3) порядок записи на границе стопа задаёт САМ КОД («последним пишется то, чья потеря дешевле»), и по нему базис идёт ПЕРЕД памятью флажка, а не пятым, как велел консилиум; (4) ширину отпечатка я заказал константой Go — это утечка пары в общий слой, закрытая `D39.50`, причём на денежном предикате. ⛔ **И урок абзацем ниже — про эту самую выдачу: приоритет пака у владельца я НЕ подтверждал.** Слот бэкенда свободен, но «слот свободен ≠ работу зоны надо заказывать»; поэтому промт лежит файлом, исполнитель не назначен, и решение о выдаче — за владельцем. ⚠ В самой выдаче, когда она состоится, повторить норму строки 13: ВЕРНУТЬ заказ, если сессия не свежая | +| 14 | бэкенд | РЕШЁННОЕ НЕ ПЕРЕ-СПРАШИВАЕТСЯ: базис решённого как ИСТОЧНИК ОТВЕТОВ (ряд 130) + страж разрыва окон (ряд 490) | textmachine-main-57 | ✅ **своими словами, десять пунктов, механика а не текст** — и ДО начала работы названы два кандидата на отказ с замером (ряд 447 как смена продуктового поведения · сторож растущей книги, чья посылка вынесена владельцу и ответа не имеет). Ошибок понимания в эхо нет: воспроизведены порядок блоков, инвариант «базис — ИСТОЧНИК ответа, не фильтр», один писатель на двух границах, инвариантность отпечатка к способу отбора при пар-данной ширине, обязательность статуса `draft` и развилка юнитов. ⛔ **РЕЛЕЙ 17.09, через считанные минуты после выдачи: база прогона A в промте была названа НЕ ТА, и сессия уже строила прибор.** Я вписал `evidence/v16-readonly/project.db` — замер: `book_id` `guzhenren-coldrun-v16`, глоссарий 53, Σ`cost_usd` 0.43611; платный A — `stand/books/bk_ROEHZBD46ALFI43E/project.db`, глоссарий **69** (число всех банковых замеров), Σ **0.419423**, сходится с ратифицированной суммой. Нашёл советчик, пере-снял я. Той же правкой доехали три ловушки материала: все `book.yaml` прогонов ссылаются на несуществующий `/home/ubuntu-26/…` (загрузка либо падает, либо ТИХО берёт конфиги головы) · вход контура `mining-contrast.zh.txt` в git отсутствует вовсе и живёт только в зеркалах прогонов · базы в WAL-режиме, копировать с `-wal`/`-shm`. ⚠ Урок мой: **материал замера я назвал по имени каталога, а не по замеру содержимого** — «evidence» и «readonly» звучали как платная улика, и этого хватило, чтобы не открыть базу. Заказ, отправляющий мерить не тот предмет, дороже битого якоря: якорь виден линтеру, неверная база — никому. ⏳ **ДОЛГ НА ЛЕНДИНГ ЭТОГО ПАКА, записан чтобы не растворился:** ⚠ **ЧИСЛО ПЕРЕ-СНЯТО ВЕЧЕРОМ: не два якоря, а ДВАДЦАТЬ ПЯТЬ, и это не долг, а движущаяся цель.** Пак 57 переписывает восемнадцать путей движка, `terminologist.go` вырос 1473 → 1549 строк, и разбор трёх состояний даёт: **22 ПЕРЕЕХАЛИ** (токен в файле есть, уехал номер — напр. `attachClassifiedGender stamps` с :1306 на :1382, `paidAfterBurns` с :899 на :975), **2 транзиентных** (жив в срезе, нет в дереве), **0 исчезнувших совсем**. ⛔ Первый мой счёт различал только два состояния и отправил 22 позиции в «неизмеренное», которое я чуть не прочитал как «всё хорошо»: **прибор, у которого корзин меньше, чем состояний у предмета, печатает правдоподобный итог и молчит о разнице.** ⇒ после лендинга пака 57 пере-нацелить ВСЕ позиции одной правкой, считая по трём состояниям, а не по двум. Прежняя редакция записи (ниже) называла два якоря — она верна по механизму и занижена по числу. Механизм: правка зоны в `backend/internal/lang/langpack.go` сдвинула файл на 12 строк, и первые ДВА якоря стали транзиентно красны — `docs/BACKEND_SETTLED_STAYS_SETTLED_SESSION_PROMPT.md` → якорь вида `langpack.go:744` на токен `kwic_per_term` (живёт на `:756`) и якорь вида `docs/BACKLOG.md:277` на токен `manifest declares no channel` (ряд 347) → та же цель `langpack.go:632`, живущая теперь на `:644`. Замер, различающий транзиент от долга: оба токена ЦЕЛЫ в срезе `HEAD` на прежних строках и мертвы только в дереве ⇒ это чужой незакоммиченный WIP в цели, о котором прибор предупреждает сам, а не моя гниль. ⛔ Чинить ДО лендинга нельзя — цель движется, и пере-нацеливание сломается при следующей её правке или при откате. ⚠ Но у второго якоря есть и НЕ транзиентная половина: путь написан не от корня (`lang/langpack.go`), и это дефект формы, переживший сегодняшнюю чистку, потому что мой шаблон искал только полные пути. ⇒ после лендинга ОДНОЙ правкой: развернуть путь и пере-нацелить оба якоря по найденным, а не вычисленным адресам | ⏳ **ВЫДАН 17.09** после опроса кандидатов (свежих нашлось ДВЕ — `57` и `78`; взята `57`, второй сказано не занимать зону). Оба рубежа пройдены: механическая сверка 13 блоков (27 якорей, все живы, токен по адресу, коротких нет) и субагент-опровергатель — **12 находок, 9 денежных, все внесены**. ⭐ Чем окупился опровергатель: (1) я вписал «текста в коде УЖЕ НЕТ, 0 совпадений» о строке, которая ЖИВА ДОСЛОВНО, — грепал по одному файлу, увидел начало длинной строки, обрезанной по ширине, и прочёл обрезок как ФАКТ; у неё вдобавок третий носитель, голден операторских сообщений, которого я не видел вовсе; (2) замер M1 был заказан на копиях купленных баз, а таблицы чанков в них НЕТ (17 и 16 таблиц) — предмет из них не строится, нужен исходник отдельным файлом; (3) порядок записи на границе стопа задаёт САМ КОД («последним пишется то, чья потеря дешевле»), и по нему базис идёт ПЕРЕД памятью флажка, а не пятым, как велел консилиум; (4) ширину отпечатка я заказал константой Go — это утечка пары в общий слой, закрытая `D39.50`, причём на денежном предикате. ⛔ **И урок абзацем ниже — про эту самую выдачу: приоритет пака у владельца я НЕ подтверждал.** Слот бэкенда свободен, но «слот свободен ≠ работу зоны надо заказывать»; поэтому промт лежит файлом, исполнитель не назначен, и решение о выдаче — за владельцем. ⚠ В самой выдаче, когда она состоится, повторить норму строки 13: ВЕРНУТЬ заказ, если сессия не свежая | | 15 | платформа | ПРОГОН НЕ ВРЁТ О СЕБЕ — ни оператору, ни покупателю, ни счёту: ряды 398 · 394 · 396 · 399 · 400 · 416 · 486 | textmachine-main-78 | ✅ эхо блока 9 отослано | ✅ **ЗАКРЫТ АКТОМ `D39.265` 17.09**, лендинг `bde8f5f` (41 файл, $0, контрактный минор **0.17.0**). Ряды 398·399·400·486 закрыты работой, 394 и 396 ПЕРЕ-РЕШЕНИЕМ, 416 половиной; заведены 493 и 494, дополнен 484. ⛔ **Гейт зоны на финальном дереве КРАСЕН и назван ГРАНИЦЕЙ, а не зеленью** — экземпляр ряда 484, оба упавших теста живут в чистом `HEAD`. Приёмка: кампания пере-снята мной 26/25/1/0 на отдельном корне её же харнессом, копия вернулась байт-идентичной, все 26 целей на месте; деньги $0 двумя путями; две линзы (слепая по заказу и диффу, вторая вне карты отчёта) дали три настоящих находки, две из них в каноне. ⚠ **МОИ ошибки, обе в акте:** взял себе правку канона, принадлежащую ЗОНЕ (откатил, дифф 0 строк, поймал ВЛАДЕЛЕЦ) и неточная посылка §4.1 промта. ⚠ Плюс три дефекта собственных приборов, каждый пойман печатью контроля рядом с числом: шапка манифеста прочитана как пары «хеш—имя» (23 ложных расхождения при 37/37 сошедшихся), греп маркера у пойманных мутаций (маркеры стоят только у выживших, намеренно), «0 скипов» из сводки, оборванной падением (в полном логе 10). ⏳ **ВЫДАН 17.09.** Собран ВЕЕРОМ из четырёх срезов за один заход — против девяти последовательных кругов, которыми я собирал предыдущий пак; это и есть разница в способе работы, а не во времени. Оба рубежа пройдены: механическая сверка 13 блоков (18 якорей с токеном, промахов 0, голых адресов 0) и опровергатель — **10 находок, 4 денежные, все внесены**. ⛔ **Самая дорогая — против меня: §4.2 заказывал ПОСТРОЕННОЕ.** Я написал, что наш собственный грейс читается как авария; вылечено 11.09 (`platform/internal/runs/reconcile.go:1226`=`return "interrupted"`, два пина в `gracekill_test.go`, коммит `00d590e`, принято `D39.242`), а ряд 394 протух и я поверил ряду вместо дерева. ⚠ Ещё три денежные: предмет капа уже стоит в регистре зоны весом `info` с прописанным лечением, а я назвал его «самым денежным» · механизм счётчика описал неверно (он живёт на ПОПЫТКЕ, а рестарт открывает новую строку, поэтому кап на попытку повторил бы дефект) · согласие нельзя сделать соразмерным силами платформы: проекции в точке решения нет по построению, и код это записал со ссылкой на эрратy, а ряд размечен двухзонным. ⚠ И ловушка, которой не было в первой редакции: словарь причин отказа закрыт в ТРЁХ носителях, включая констрейнт базы, и не стережётся НИ ОДНИМ гейтом (0 хитов при контроле 12 у соседа) — четвёртое значение прошло бы `make check` зелёным и упало в проде | | 16 | бэкенд | АНГЛИЙСКАЯ ПАРА en→ru: слой ДАННЫХ английского языка как ПРОДУКТ (слово владельца 17.09 — «данные надо написать… уникально описать английский язык… от этого качество перевода зависит»), две стены `EXIT 10` (нет промт-пакета пары · гейт терминолога при пустом паке), порог эха `0.15` унаследован от китайского бенчмарка, второй детектор латиницы без гейта, нарезка книги Кристоффа | `textmachine-main-c5` | ✅ **эхо принято 17.09** — своими словами, семь пунктов, механика а не текст; ⭐ в нём взята ПОПУЛЯЦИЯ ГРУППЫ, а не документа, то есть моя собственная ошибка первой редакции НЕ унаследована, и сквозная проверка манифеста поставлена ПЕРЕД письмом данных | 🔴 **ИДЁТ** — выдан после акта `D39.266`, слот освободил акт, а не сдача работы. Два рубежа промта пройдены: механическая сверка 13 блоков (29 адресов, битых 0) и субагент-опровергатель — **15 находок, 9 приняты, 6 уточнены моими числами**, из них ЧЕТЫРЕ мои ошибки ПОПУЛЯЦИИ (документ вместо группы · отозванная формулировка ряда 315 прочитана как живая · строка ноты обрезана на 200 знаках и объявлена «телом без содержимого» · сравнение лучшего префикса с перелётом). ⚠ Fable 5 разрешён исполнителю ОДИН — владельцем и именно под верификацию ДАННЫХ. $0 по умолчанию; единственная платная проба — через моё слово. ⛔ Открытый вопрос владельцу, сессии сообщён: размеченного корпуса для en→ru нет вовсе, человеческий эталон вне репозитория ⇒ качество данных числом не предъявимо, и самодельную метрику заводить запрещено | @@ -264,7 +264,7 @@ UNKNOWN 1 — её, дошёл один раз до правок; десять (замерено: `A-after` на 16 при голове 18), и дальше развилка, у которой обе ветви плохи: - `store.OpenReadOnly` (`backend/internal/store/store.go:161`=`current != len(migrations)`) на несовпадении **ОТКАЗЫВАЕТ** `SchemaMismatchError` — прочитать улику им нельзя; -- обычный `store.Open` идёт через миграцию (`backend/internal/store/migrate.go:673`=`for v := current; v < len(migrations); v++`) +- обычный `store.Open` идёт через миграцию (`backend/internal/store/migrate.go:714`=`for v := current; v < len(migrations); v++`) и **МОЛЧА ПРИВОДИТ СХЕМУ К ГОЛОВЕ** — то есть переписывает купленный прогон, и улика перестаёт быть уликой. ⇒ **Норма:** улики платных прогонов читаются собственным ридером на `mode=ro&immutable=1`, замеры снимаются на diff --git a/docs/research/31-harness-independent-critique.md b/docs/research/31-harness-independent-critique.md index 2d606f91..6c54b50e 100644 --- a/docs/research/31-harness-independent-critique.md +++ b/docs/research/31-harness-independent-critique.md @@ -59,7 +59,7 @@ Контрпример проверен исполнением: две книги различаются персонажем в предыдущей главе; следующая начинается одинаковым «终于答应了» — «наконец согласился/согласилась». Реальный `TranslateBook` с текущими zh→ru промптами и подставным провайдером дал разные запросы предыдущих единиц и побайтно одинаковые запросы текущих переводчика и редактора. Условия: пустой одинаковый банк, майнинг выключен, банкнота включена; две главы и две редакторские единицы на вариант. Это проверка доступности информации; частоту неправильных переводов живой модели она не устанавливает. Артефакт и воспроизведение — в конце документа. -Отрицательные результаты carryover не закрывают весь класс: в exp15 широкое чтение, lookahead и состояние сцены остались неисполненными (`docs/experiments/15-segmentation-empirics.md:489`=`не исполнены (бюджет/приоритет`). Сам target-architecture ограничивает перенос результатов классом исследованных книг (`docs/architecture/09-target-architecture.md:138`=`НИКОГДА не переносится в продукт`). Работа над структурой глав полезна, но она отвечает на другой вопрос. +Отрицательные результаты carryover не закрывают весь класс: в exp15 широкое чтение, lookahead и состояние сцены остались неисполненными (`docs/experiments/15-segmentation-empirics.md:489`=`не исполнены (бюджет/приоритет`). Сам target-architecture ограничивает перенос результатов классом исследованных книг (`docs/architecture/09-target-architecture.md:142`=`НИКОГДА не переносится в продукт`). Работа над структурой глав полезна, но она отвечает на другой вопрос. **Коррекция:** проверить зависимости через границы редакторских единиц: субъект, адресат, намеренная неоднозначность, смена регистра, позднее раскрытие смысла термина. Сначала сравнить текущий вход с минимальным необходимым исходным контекстом; затем выбирать окно, поиск исходных фрагментов или состояние сцены. Не начинать со строительства универсальных summaries/графа. В строке 80 бэклога (`docs/PROGRESS.md:153`) уточнить: ссылка на исходник проверяется детерминированно, её семантическое толкование — нет. Требование безошибочного детерминированного толкования заранее заблокирует исследование этой задачи. diff --git a/docs/research/35-bank-memory-consilium.md b/docs/research/35-bank-memory-consilium.md index 04851065..c4539ea4 100644 --- a/docs/research/35-bank-memory-consilium.md +++ b/docs/research/35-bank-memory-consilium.md @@ -1646,10 +1646,10 @@ D39.50 (просьба владельца против «до него kwic не §П3 утверждает: «Исхода движка „это не термин“ нет и не вводится. Класс отказ-сигналов закрыт D39.102 п.2». **Это верно про НОТУ и неверно про ДЕРЕВО.** В боевом проводе zh-ru отказ-сигнал жив и приглашается самим промтом: -`backend/prompts/zh-ru/terminologist.md:36`=`не можешь выбрать перевод уверенно` («верни ⟦TM-NO-DST⟧… это нормальный»), +`backend/prompts/zh-ru/terminologist.md:32`=`не можешь выбрать перевод уверенно` («верни ⟦TM-NO-DST⟧… это нормальный»), парсер ждёт его вместе с искажёнными формами (`backend/internal/terminology/terminology.go:694`=`const NoDst = "⟦TM-NO-DST⟧"`, `backend/internal/terminology/terminology.go:1248`=`func isNoDst`), а счёт печатается прогоном -(`backend/internal/pipeline/terminologist.go:649`=`"declined", res.Declined`). +(`backend/internal/pipeline/terminologist.go:806`=`"declined", res.Declined`). ⚠ Различение, без которого поправка читается грубее, чем есть: §П3 прав в том, что у СТРОКИ БАНКА нет исхода «не термин» — отказ роли не становится решением, а превращается в передачу черновика (это сам отчёт и говорит абзацем ниже). Неверно @@ -1691,7 +1691,7 @@ D39.50 (просьба владельца против «до него kwic не номера строк, а в этот итог целятся якоря других доков. **Уехал один адрес.** Счёт отказов терминолога печатается на -`backend/internal/pipeline/terminologist.go:702`=`"declined", res.Declined` — в теле стоит `:649`. Причина: пак «лестница +`backend/internal/pipeline/terminologist.go:806`=`"declined", res.Declined` — в теле стоит `:649`. Причина: пак «лестница попытки» (акт `D39.264`) вынес цикл повторной попытки в `backend/internal/pipeline/attemptladder.go` и сдвинул строки файла. **Утверждение тела при этом верно и не меняется** — отказ-сигнал жив в боевом проводе zh-ru, приглашается промтом роли и считается прогоном; устарел только способ найти строку.