From 4496525c5da6ae6f86150f8bbc2ca023335818b2 Mon Sep 17 00:00:00 2001 From: heaven Date: Wed, 16 Sep 2026 21:39:53 +0300 Subject: [PATCH] Give five more backlog anchors their expectation tokens --- docs/BACKLOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 73b8feaa..cb745234 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -277,11 +277,11 @@ | 347 | **ПАР-ДАННЫЕ НЕЛЬЗЯ ЗАВЕЗТИ ПО ЧАСТЯМ: манифест требует ≥1 канал, и транслитерационный канал зашит в китайскую форму.** `lang/langpack.go:632`=`manifest declares no channel` — «manifest declares no channel — omit the file to ship all channels, or list at least one»; каналов два и они закрыты в Go (`source-morphology`, `transliteration`, `:108-114`), а `validate()` при `transliteration` требует ПОИМЁННО пиньинь-категории (`:521-527`); морфологический канал просит общие таблицы (`:507-516`) и в китайскую форму не зашит. Отказ жёсткий: при `gates.terminology.enabled` и `pack == nil` прогон отказывает `RefusalBadConfig` (`backend/internal/pipeline/runner.go:364`=`Gates.Terminology.Enabled` — ⚠ испр. 16.09: было :349), а в боевом `pipeline-c1.yaml:151`=`Роль ТЕРМИНОЛОГА (пак-20, D39.42 п.1)` гейт включён ⇒ вторая пара выбирает между фиктивными данными китайской формы и прогоном БЕЗ банка терминов (продуктовая цель №1). ⚠ **Триггер — первая новая ИСХОДНАЯ пара, и он РАНЬШЕ строки 115:** манифест лежит в `//` и хешируется языком-ИСТОЧНИКОМ (`langpack.go:252`), а 115 — про новую ЦЕЛЬ; ближайшая исходная пара в трекере — ja→ru (строка **81**), цель у неё ru. ⚠ **Пять строк снимают отказ, но банка на не-Han источнике НЕ дают** — детектора нет (строка **35**: en-детектор не существует, ja с zh-таблицами активно неверен); заказывать вместе, иначе строка обещает больше, чем покупает. Сегодня не оплачено ни разу: 0 `manifest.txt` в `configs/`, каталог пары один. ⚠ D39.64 П4 ратифицировала ОБРАТНОЕ намерение («источник без пиньинь-морфологии грузится без Palladius-файлов») — строка `list at least one` не дошла шаг до собственной цели | бэкенд | когда-нибудь (вход пака второй пары; триггер общий со строками 81 и 35) | разрешение нулевого числа каналов + решение по форме каналов | research/33 Д7, D39.64 П4 | | 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, на который ссылаются `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 | | 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 | **ПЯТЬ РУКОПИСНЫХ СПИСКОВ АРМ-КОНФИГОВ ЖИВУТ РЯДОМ С ДВУМЯ ГЕЙТАМИ, КОТОРЫЕ ТЕПЕРЬ ГЛОБЯТ.** `pipeline/promptlabel_test.go:45`=`pipeline-arm-glm.yaml` · `config/echoregen_shipping_test.go:35` · `config/content_labels_test.go:529` и `:575` · `config/prompt_pack_test.go:239` перечисляют армы поимённо; новый арм автоматически попадает в `TestAnArmIsTheProductionConfigWithADifferentEditor` и `TestSwapArmConfigs` и проходит мимо всех пяти. Найдено приёмкой `D39.225`, вне заказа пака; предсуществующее | бэкенд | когда-нибудь (со следующим касанием армов) | свести пять списков к глобу | `D39.225` | +| 352 | **ПЯТЬ РУКОПИСНЫХ СПИСКОВ АРМ-КОНФИГОВ ЖИВУТ РЯДОМ С ДВУМЯ ГЕЙТАМИ, КОТОРЫЕ ТЕПЕРЬ ГЛОБЯТ.** `pipeline/promptlabel_test.go:45`=`pipeline-arm-glm.yaml` · `config/echoregen_shipping_test.go:35`=`pipeline-arm-glm.yaml` · `config/content_labels_test.go:529`=`pipeline-c1.yaml` и `:575` · `config/prompt_pack_test.go:239`=`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 | | 354 | ⛔ **ОПЛАЧЕННЫЙ `2xx` С ОБОРВАННЫМ ТЕЛОМ — КЛАСС, СТОИВШИЙ 32 % ЖИВОГО ПРОГОНА, И СТРАНИЦА КВИРКОВ О НЁМ МОЛЧИТ.** Живой прогон 08.09 (10 глав, `committed=$1.259217`): два вызова редактора вернулись `2xx` с нечитаемым JSON и были ОПЛАЧЕНЫ — `$0.407606`, то есть **32 % бюджета прогона**. Движок класс ЗНАЕТ и обрабатывает штатно: `backend/internal/pipeline/disposition.go:80`=`FlagDecodeError FlagReason` `FlagDecodeError` — «2xx with an unreadable body — billed, conservatively settled, flagged». ⛔ Но `docs/experiments/00-provider-quirks.md` — страница, которую канон велит читать ПЕРЕД правкой адаптеров и вызовов, — о нём не несёт НИ СТРОКИ: греп по `decode`/`unexpected end of JSON`/«оборван» даёт **0 при 250 строках файла** (контроль: слово `provider` там встречается 3 раза, то есть спрошено существующее). ⇒ следующая сессия, читающая квирки перед платным прогоном, не узнает о поведении, которое съедает треть бюджета. ⛔ **испр. 08.09: МЕХАНИЗМ НАЗВАН ЗДЕСЬ НЕВЕРНО — это не свойство провайдера, а дефект движка, и носитель у него теперь строка 360.** Полигон замером exp-23 п.5 показал, что за подписью «нечитаемое тело» стоит ТАЙМАУТ ЧТЕНИЯ по `attempt_s`, который движок сам же выбрасывает в `_`. ⇒ эта строка остаётся ТОЛЬКО про пробел страницы квирков: она обязана предупреждать, ЧТО оператор увидит и почему, со ссылкой на 360, а не приписывать поведение вендору. ⚠ Зона — ПОЛИГОН (`docs/experiments/` по зонной таблице), живой сессии полигона нет; строка заведена оркестратором как носитель, писать в файл полигону | полигон | скоро (перед следующим платным прогоном любой зоны) | строка в `00-provider-quirks.md` с частотой, стоимостью и тем, что движок делает с классом | живой прогон 08.09, пак «правда оператору и деньги» | | 356 | **КНИГА У СВОЕГО ПОЖИЗНЕННОГО ПОТОЛКА РОЛИ НАВСЕГДА ОСТАЁТСЯ БЕЗ КОНСОЛИДИРОВАННОГО БАНКА, и ни одно сообщение не говорит именно этого.** `store/ledger.go` `RoleSpentUSD` суммирует чекпойнты роли за ВСЮ жизнь книги, предполётный срез сеет `probe := spent` и обрывается на `probe+want > budgetUSD`; при `fits == 0` цикл попыток не запускается вовсе. ⚠ Оператор ПРИ ЭТОМ извещён — `terminologist.go:936` печатает `batches_planned=N batches_running=0 batches_dropped=N spent_usd budget_usd` ДО первого вызова (сверено оркестратором 08.09), поэтому «тихо» здесь неверно, и находка приёмки в этой части ОТКЛОНЕНА доводом зоны. Верен остаток: сообщение говорит про ЭТОТ прогон, а не про то, что книга исчерпала пожизненный бюджет роли и консолидации не получит НИКОГДА, сколько её ни перезапускай. Свойство `budget_usd`, а не фильтра пака «правда оператору и деньги»: тот на решение бюджета не влияет. ⇒ решить, что показывать оператору и владельцу: отдельное сообщение «книга у пожизненного потолка роли», подъём потолка как явный акт, или сознательное молчание с записью | бэкенд | когда-нибудь (гейт: первая книга, упёршаяся в пожизненный потолок роли) | сообщение об исчерпании пожизненного бюджета либо ратифицированное молчание | приёмка `D39.226`, линза «вне карты» | -| 359 | ⛔ **ЯКОРНЫЙ ЛИНТ НА ГРЯЗНОМ ДЕРЕВЕ НЕ ДАЁТ ЧИСЛА: ЗА ОДИН СЕАНС 11.09 ОН ВЫДАЛ 19 → 15 → 11 НА ОДНИХ И ТЕХ ЖЕ 123 ЖИВЫХ ДОКАХ.** Ряд утверждал «6 проблемных якорей при 121 живом доке» (08.09) — протух; но сменившее его число протухало ТРИЖДЫ за час, пока его мерили. Причина названа собственным футером прибора: цели читаются из ДЕРЕВА, а дерево одновременно правят движок, платформа и контракт — чужой незакоммиченный WIP и роняет токен, и спасает его. ⇒ **норма: якорный линт судят по `HEAD` либо по ЧИСТОМУ дереву; число с грязного — снимок, а не факт, и в доке оно живёт только с датой и с этой оговоркой.** ⛔ **И развилка, которую сеанс прошёл в ОБЕ стороны — нормы нет ни одной, вопрос владельцу.** Четыре якоря зоны оркестратора (`BACKLOG.md:120`, `:127`, `:256` → `openapi.yaml:2191`/`:2367`/`:3159`; якорь в теле `D39.212` → `runs.go:479`) были ВЕРНЫ в `HEAD` и «красны» только от WIP соседа; коммит `bdc1547` по пингу зоны пере-навёл их ВПЕРЁД, на позиции ДЕРЕВА (`:2200` · `:2376` · `:3168` · `runs.go:490`), и теперь они верны в дереве и НЕВЕРНЫ в `HEAD`. Обе стратегии законны — «ждать лендинга и судить по `HEAD`» либо «наводить на то, что вот-вот сядет», — но выбрать надо ОДНУ, иначе прибор красит по очереди то одних, то других. ⭐ **Половина (б) ЗАКРЫТА:** `backend/docs/DISCLOSURE_LAW_DESIGN.md:40` пере-наведён с `:233` на `quality.go:235`; в `HEAD` `:235` ВЕРЕН, и красным его делает только WIP движка — то есть единственный сегодняшний бэкендовый ✗ ложный. **Остаток на 11.09 — 11 ✗, из них НАСТОЯЩИХ 10:** `docs/experiments/` **7** + фронт **3** (зона ЗАМОРОЖЕНА). **Восемь пере-наводятся адресом** (снято по `HEAD` `bdc1547`: `httpllm.go` 517→**739**, 528→**750** · `models.yaml` 137→**173** · `export.go` 176→**178** · `14-api-contract/README.md` 1054→**1097** · `openapi.yaml` 1606→**1624**), **два пере-писываются по факту:** `23-editor-tier.md:7739`/`:7740` — «×2 дешевле glm» в `pipeline-c1.yaml:95` и `pipeline-arm-glm.yaml:6` ИСЧЕЗЛО (файлы живы, 250 и 83 строки, греп 0; обе строки удалил коммит `7ffec51`, где они видны со знаком «−»). ⚠ **Прежняя атрибуция этого снятия к `D39.226` НЕ подтверждается телом ноты:** в нём ноль вхождений `glm`, «ценов», «дешевл» (контроль: «пак» — 5) — переписывать по ФАКТУ конфига, а не по этой ссылке. ⚠ Все адреса выше сняты по `HEAD` и протухнут при следующем лендинге — пере-снимать в момент правки, а не верить строке. ⛔ **ПОЛИГОННЫЕ 7 БЕЗ ХОЗЯИНА:** зона выведена из скоупа целиком (`D39.248` п.4), доки `docs/experiments/` остались в дереве, а перечень унаследованного другими зонами в той ноте — ряды 413·414·415·416·417·422 — ЭТОТ ряд не называет. Каталог лежит под `docs/`, то есть по умолчанию оркестратора, но зонная таблица отдавала `docs/experiments/` полигону ⇒ **сдвиг границы зоны, которого не объявляла ни одна нота, и назначать его себе молча нельзя** | фронт (3, ЗАМОРОЖЕН); полигонные 7 — БЕЗ ХОЗЯИНА; выбор нормы якорения — владельцу | когда-нибудь (8 адресов — механическое пере-наведение, попутно с касанием файлов) | выбрана ОДНА норма якорения (`HEAD` либо «вперёд») · 8 адресов пере-наведены · 2 полигонных пере-написаны по факту · хозяин `docs/experiments/` после `D39.248` назван явно | `counts.py --lint` 08.09; пере-замеры 11.09 — 19/15/11 на 123 доках, разбор по `HEAD` `bdc1547` | +| 359 | ⛔ **ЯКОРНЫЙ ЛИНТ НА ГРЯЗНОМ ДЕРЕВЕ НЕ ДАЁТ ЧИСЛА: ЗА ОДИН СЕАНС 11.09 ОН ВЫДАЛ 19 → 15 → 11 НА ОДНИХ И ТЕХ ЖЕ 123 ЖИВЫХ ДОКАХ.** Ряд утверждал «6 проблемных якорей при 121 живом доке» (08.09) — протух; но сменившее его число протухало ТРИЖДЫ за час, пока его мерили. Причина названа собственным футером прибора: цели читаются из ДЕРЕВА, а дерево одновременно правят движок, платформа и контракт — чужой незакоммиченный WIP и роняет токен, и спасает его. ⇒ **норма: якорный линт судят по `HEAD` либо по ЧИСТОМУ дереву; число с грязного — снимок, а не факт, и в доке оно живёт только с датой и с этой оговоркой.** ⛔ **И развилка, которую сеанс прошёл в ОБЕ стороны — нормы нет ни одной, вопрос владельцу.** Четыре якоря зоны оркестратора (`BACKLOG.md:120`, `:127`, `:256` → `openapi.yaml:2191`/`:2367`/`:3159`; якорь в теле `D39.212` → `runs.go:479`) были ВЕРНЫ в `HEAD` и «красны» только от WIP соседа; коммит `bdc1547` по пингу зоны пере-навёл их ВПЕРЁД, на позиции ДЕРЕВА (`:2200` · `:2376` · `:3168` · `runs.go:490`), и теперь они верны в дереве и НЕВЕРНЫ в `HEAD`. Обе стратегии законны — «ждать лендинга и судить по `HEAD`» либо «наводить на то, что вот-вот сядет», — но выбрать надо ОДНУ, иначе прибор красит по очереди то одних, то других. ⭐ **Половина (б) ЗАКРЫТА:** `backend/docs/DISCLOSURE_LAW_DESIGN.md:40`=`unknown, not zero` пере-наведён с `:233` на `quality.go:235`; в `HEAD` `:235` ВЕРЕН, и красным его делает только WIP движка — то есть единственный сегодняшний бэкендовый ✗ ложный. **Остаток на 11.09 — 11 ✗, из них НАСТОЯЩИХ 10:** `docs/experiments/` **7** + фронт **3** (зона ЗАМОРОЖЕНА). **Восемь пере-наводятся адресом** (снято по `HEAD` `bdc1547`: `httpllm.go` 517→**739**, 528→**750** · `models.yaml` 137→**173** · `export.go` 176→**178** · `14-api-contract/README.md` 1054→**1097** · `openapi.yaml` 1606→**1624**), **два пере-писываются по факту:** `23-editor-tier.md:7739`/`:7740` — «×2 дешевле glm» в `pipeline-c1.yaml:95` и `pipeline-arm-glm.yaml:6` ИСЧЕЗЛО (файлы живы, 250 и 83 строки, греп 0; обе строки удалил коммит `7ffec51`, где они видны со знаком «−»). ⚠ **Прежняя атрибуция этого снятия к `D39.226` НЕ подтверждается телом ноты:** в нём ноль вхождений `glm`, «ценов», «дешевл» (контроль: «пак» — 5) — переписывать по ФАКТУ конфига, а не по этой ссылке. ⚠ Все адреса выше сняты по `HEAD` и протухнут при следующем лендинге — пере-снимать в момент правки, а не верить строке. ⛔ **ПОЛИГОННЫЕ 7 БЕЗ ХОЗЯИНА:** зона выведена из скоупа целиком (`D39.248` п.4), доки `docs/experiments/` остались в дереве, а перечень унаследованного другими зонами в той ноте — ряды 413·414·415·416·417·422 — ЭТОТ ряд не называет. Каталог лежит под `docs/`, то есть по умолчанию оркестратора, но зонная таблица отдавала `docs/experiments/` полигону ⇒ **сдвиг границы зоны, которого не объявляла ни одна нота, и назначать его себе молча нельзя** | фронт (3, ЗАМОРОЖЕН); полигонные 7 — БЕЗ ХОЗЯИНА; выбор нормы якорения — владельцу | когда-нибудь (8 адресов — механическое пере-наведение, попутно с касанием файлов) | выбрана ОДНА норма якорения (`HEAD` либо «вперёд») · 8 адресов пере-наведены · 2 полигонных пере-написаны по факту · хозяин `docs/experiments/` после `D39.248` назван явно | `counts.py --lint` 08.09; пере-замеры 11.09 — 19/15/11 на 123 доках, разбор по `HEAD` `bdc1547` | | 361 | **ДВА ГЕЙТА ОДНОГО ФАЙЛА ТЯНУТ ЯЧЕЙКУ В РАЗНЫЕ СТОРОНЫ: экранирование `\|` по счёту колонок ЛОМАЕТ сверку токена якоря.** Счёт ячеек требует экранировать черту в тексте ячейки; сверка якоря сравнивает токен с кодом ДОСЛОВНО, а в коде черта без слеша. ⇒ токен, внутри которого есть `|`, после законного экранирования перестаёт совпадать с целью, и линтер кричит «цель уехала» на невиновного. Замерено платформенной сессией 08.09 на `PD-422`: якорь `internal/runs/runs.go:408` держал токен `resnapshot := book.BankMoved || book.HasPriorRun`; после экранирования черт по §4.5 токен перестал совпадать, и сессия починила это УКОРОЧЕНИЕМ токена — то есть заплатила точностью якоря за форму таблицы. ⇒ следующий, кто пойдёт экранировать черты, наступит на то же и так же потеряет точность. **Направление:** `norm()` в `docs/scripts/counts.py` снимает экранирование `\|` → `|` перед сверкой — шаг детерминированный и симметричный, как и схлопывание пробелов, которое та же функция уже делает по тому же доводу; докстринг «пробельная нормализация — и только она» правится вместе с поведением, иначе останется вторым носителем. ⚠ Укороченный токен `PD-422` после починки можно вернуть к полному | оркестратор | скоро | `norm()` снимает экранирование, селф-тест на случай `a \|\| b`, докстринг переписан | находка платформенной сессии 08.09 при исполнении пака разреза приёма | | 362 | **РЕМОНТ ПРОТИВ РЕМОНТА: несколько ремонтов одного чанка покупают ДОКАЗУЕМО НЕПЕРЕСЕКАЮЩИЕСЯ спаны и вытесняют друг друга в `superseded`.** Третий член семьи строк 355/358: адрес `{глава, чанк, стадия}` предполагает, что поздний вызов ЗАМЕЩАЕТ ранний, а ремонт переписывает ОГРАНИЧЕННЫЙ спан, не весь выход стадии. ⚠ Механизм здесь НЕ пересказан — он расписан в самом коде, `backend/internal/pipeline/paidtail.go` (абзац «The repair role shares that address WITHOUT sharing that property»), и там же названо, почему очевидное лечение не работает: разведение по РОЛИ ремонты между собой не различает — «every one of them carries the same role and only the span tells them apart». ⛔ Нынешнее поведение ЗАПИНЕНО КАК НЕПОДВИНУТОЕ, а не как верное — `TestTheRoleSplitDoesNotMoveARepairsMoney`; то есть пин охраняет отсутствие тихого сдвига, а не правильность. **Цена, замер ревьюера движковой сессии 08.09: 77 % денег чанка отчитаны как «купившие ничего»** ⚠ (оркестратором НЕ пере-мерено — взято со слов замера, механизм проверен по коду). Не взято в пак «движок уже знает» сознательно: вторая денежная поверхность в одном лендинге | бэкенд | скоро | ремонты одного чанка различены по спану, а не по триплету; пин утверждает ПРАВИЛЬНОСТЬ, а не неподвинутость | приёмка пака «движок уже знает» 08.09 | | 363 | **ПОЛНЫЙ ТЕКСТОВЫЙ САЙДКАР МОЛЧИТ О НЕПОЛНОТЕ БАНКА — а баннер зовёт в него ровно тогда, когда строк больше капа.** После пака «движок уже знает» факт неполноты несут машинный сайдкар и stdout-экран; текстовый сайдкар стоп-таблицы выведен из заказа §4.1 сознательно и остался единственной поверхностью, которая о неполноте не говорит. ⚠ Цена именно в связке: stdout капирован 20 строками, и когда строк больше, баннер отправляет читателя в ПОЛНЫЙ текстовый сайдкар — то есть туда, где факта нет. ⇒ на большой книге оператор, послушавшийся баннера, теряет ровно то, что пак довозил | бэкенд | скоро | текстовый сайдкар несёт полноту наравне с двумя другими поверхностями | пинг движковой сессии, приёмка пака «движок уже знает» 08.09 | @@ -293,7 +293,7 @@ | 370 | **Anthropic-адаптер не несёт капа ретраев, который есть у общего транспорта.** `backend/internal/llm/provider_anthropic.go` возвращает `BilledDecodeError` с `retryable=true` без ограничителя `billedDecodeSeen`, который стоит в `httpllm.go` — то есть на этом адаптере оплаченный 2xx может повториться до `max_attempts`, а не один раз. ⚠ **Боевой цены сегодня НОЛЬ:** поверхность deprecated, Anthropic убран из стека 04.07, вызовов по этому пути в прогонах нет. ⇒ сознательно вынесено ИЗ пака «вызов, который оборвали мы» (строка 360) по доводу ревью: пак получил бы тесты и мутации на мёртвую поверхность. Чинить при воскрешении адаптера — тогда же и проверять, применим ли к нему признак доставки | бэкенд | когда-нибудь | адаптер получает тот же кап ЛИБО удалён вместе с поверхностью | ревью пака «вызов, который оборвали мы», 08.09 | | 372 | **СТРАЖ ОТМЕНЫ ГЛОТАЕТ НАСТОЯЩУЮ ДЕНЕЖНУЮ ОСТАНОВКУ, когда прогон отменил не человек, а упавший сосед.** Платформа получит `failed` вместо `paused`, а книга без денег упрётся в потолок на ПЕРВОЙ резервации следующего прогона. Сессия стража оставила и была права: ратифицированный комментарий ветки `waitAborted` требует ровно этого для ОБОИХ случаев. ⇒ предмет не движковый, а **продуктовое решение о шве**: должна ли платформа отличать «остановлено человеком» от «остановлено падением соседа» ⛔ **ЗАМЕР 10.09 (движковая сессия, ЧТЕНИЕМ; пере-снят оркестратором): в дереве ЖИВЁТ УТВЕРЖДЕНИЕ, которое дерево же опровергает.** Источников отменённого контекста **ДВА**, и по `ctx.Err()` они неотличимы: человеческий сигнал (`backend/cmd/tmctl/main.go`, греп `signal.NotifyContext`) и **собственная отмена волны** — `runWave` заводит производный контекст (`backend/internal/pipeline/waverun.go`, греп `context.WithCancel(parent)`) и отменяет его из ветвей инфра-ошибки соседа и паники. При этом ДВА не-тестовых комментария прямо декларируют, что источник один: `backend/internal/pipeline/bankdecisions.go`, греп `by nothing else`, и `backend/cmd/tmctl/main.go`, греп `a cancelled run is one somebody asked to stop`. ⇒ ни `exitCode`, ни `terminal()` двух причин не различают, а комментарии учат следующего, что различать нечего. ⚠ Полный инвентарь носителей отмены (4 источника, около двадцати читателей, с адресами по HEAD) — в секции движковой зоны `docs/PROGRESS.md`; он вход любого пака про корректность остановки | бэкенд | скоро | шов различает две отмены ЛИБО объявлено, что не различает, с ценой | сдача пака «вызов, который оборвали мы» 08.09 | | 373 | **НА HTTP/2 НАШ ДЕДЛАЙН ИНЕРТЕН, если тело запроса больше окна пира, а провайдер отвечает ранним 2xx, не дочитав его.** Замер движковой сессии: вызов длится столько, сколько держит сервер — **4 с и 8 с при дедлайне 700 мс**, — и всё уезжает в `decode_error`, ту самую дверь, которую пак 360 объявил закрытой. Механизм: запись паркуется в `awaitFlowControl` на `sync.Cond`, которую контекст НЕ будит. ⚠ Дыра ПРЕДШЕСТВУЕТ паку и требует пирa, отвечающего не дочитав тело (НЕ форма DeepSeek). Лечится `http2.Transport.WriteByteTimeout`, но его значение — НОВЫЙ порог, а пороги назначать нельзя | бэкенд | когда-нибудь | дедлайн действует и на записи тела ЛИБО класс объявлен недостижимым с замером | сдача пака «вызов, который оборвали мы» 08.09 | -| 375 | **ВЫГРУЗКА ЧИТАЕТ ОТМЕНЁННУЮ ПОЗИЦИЮ КАК ВЫПАВШИЙ КУСОК ТЕКСТА.** `memberDrops` считает строку `cancelled` выпавшим членом редакторской единицы — то есть выносит КОНТЕНТ-вердикт по факту нажатия кнопки: читателю сообщается, что из единицы пропал текст, тогда как пропала лишь незаконченная работа. Семантика экспорта, не транспорт. ⚠ Исчезает само вместе с классом `cancelled`, если денежная половина строки **360** будет урезана. Найдено критиком полноты седьмого круга; диспозиция оркестратора жила письмом и носителя не имела до `D39.231` ⚠ **ДОБАВКА 10.09 (замер движковой зоны по запросу оркестратора, подтверждён ключом БД).** Правка «применить `resolvedForResume` внутри `memberDrops`» ПОЛНА для этого пути (живой вызов один — `backend/internal/pipeline/export.go:307`) и попутно чинит артефакт порядка: внутренний цикл `status.go:538-543` оставляет ПОСЛЕДНЮЮ флагнутую черновую строку, а строки приходят `ORDER BY … stage`, поэтому при двух черновых стадиях `cancelled` может перебить контентную причину; фильтр возвращает контентную. Случай «сверху лёг `cancelled`» НЕВОЗМОЖЕН: строка апсертится по `(book_id, chapter, chunk_idx, stage)` (`backend/internal/store/chunkstatus.go:92`), а контентно флагнутая стадия не пере-выполняется (резюм-гейт `backend/internal/pipeline/stagerun.go:93`). ⛔ **НО правка меняет ЛОЖЬ на МОЛЧАНИЕ, и это не закрытие:** если единственный флаг члена — `cancelled`, после фильтра `DroppedMembers` = 0 и `DroppedReason` пуст (`export.go:320-322`), единица уходит `ok`, молча не досчитавшись текста члена. ⇒ закрывать надо ТРЕТЬИМ состоянием («единица неполна из-за остановки»), а не одним фильтром. Окно узкое: нужна комбинация «финальная строка от прежнего прогона + редрайв члена + стоп» (`export.go:174`) ⛔ **ВТОРАЯ ПОЛОВИНА, вытащенная 10.09 из отменённого пака, чтобы не умереть вместе с ним (ПОДОЗРЕНИЕ — выведено ЧТЕНИЕМ кода движковой сессией, фикстурой НЕ гонялось):** строка `chunk_status` не добавляется, а ЗАМЕЩАЕТСЯ — апсерт по ключу стадии с `DO UPDATE SET … final_hash = excluded.final_hash` (`backend/internal/store/chunkstatus.go`, греп `on conflict`), а запись отмены кладёт ПУСТОЙ `final_hash`. ⇒ прогон, который ПЕРЕ-атакует позицию с уже готовой строкой (редрайв · пере-пин с `--resnapshot` · правка исходника) и будет оборван, оставляет позицию с пустым хешем — выгрузка теряет текст, чекпойнт которого лежит на диске. То есть отмена способна стереть НАСТОЯЩИЙ вердикт, а не только пометить незаконченное. Проверяется фикстурой «готовая строка → редрайв → обрыв» ⚠ **ИСПР. 11.09 — ОДИН ИЗ ТРЁХ МАРШРУТОВ ОПРОВЕРГНУТ ЧТЕНИЕМ, и опровергла его АВТОР прежней формулировки:** через РЕДРАЙВ сценарий НЕДОСТИЖИМ. `ResetChunkStages` (`backend/internal/store/chunkstatus.go`, греп `func (s *Store) ResetChunkStages`) в ОДНОЙ транзакции удаляет и `checkpoints`, и `chunk_status` сбрасываемых стадий — пере-снято мной: две `DELETE` между `BeginTx` и `Commit`; то же говорит доккоммент `backend/internal/pipeline/volume.go` (греп `DELETES the unit`). ⇒ к моменту пере-атаки замещать нечего, прежнего вердикта уже нет. **Остаются ДВА маршрута**, оба обходят быстрый путь резюма при ЖИВОЙ прежней строке: пере-пин `--resnapshot` (снапшот уехал, признак пере-пинуемости ложен) и правка исходника или промта (не сходится хеш содержимого). Их и предъявляет фикстура; исход «недостижимо» — такой же результат ⚠ **РАСШИРЕН 16.09 актом `D39.255`: то же и для МЕТКИ СТОПА.** `memberDrops` считает выпавшим члена с ЛЮБОЙ флагованной черновой строкой и причину не спрашивает (пере-снято приёмкой на обоих деревьях: `cancelled` → 1, `retry_unaffordable` → 1, контроль `hard_refusal` → 1; предикат байт-идентичен HEAD). ⇒ читатель получает «ТРЕБУЕТ ПРОВЕРКИ ЧЕЛОВЕКОМ», а оператор — «missing for good … cannot bring those back» там, где следующий прогон с поднятым потолком доделает и члена, и эдит. Верная починка — третье состояние члена («не доделан, но доделается»); фильтр в `memberDrops` меняет ложь на молчание.| бэкенд | скоро | единица, неполная ИЗ-ЗА ОСТАНОВКИ, отличима и от выпавшего контента, и от полной: ни ложного контент-вердикта, ни молчания | седьмой круг ревью пака 360, 08.09; добавка — замер зоны 10.09 | +| 375 | **ВЫГРУЗКА ЧИТАЕТ ОТМЕНЁННУЮ ПОЗИЦИЮ КАК ВЫПАВШИЙ КУСОК ТЕКСТА.** `memberDrops` считает строку `cancelled` выпавшим членом редакторской единицы — то есть выносит КОНТЕНТ-вердикт по факту нажатия кнопки: читателю сообщается, что из единицы пропал текст, тогда как пропала лишь незаконченная работа. Семантика экспорта, не транспорт. ⚠ Исчезает само вместе с классом `cancelled`, если денежная половина строки **360** будет урезана. Найдено критиком полноты седьмого круга; диспозиция оркестратора жила письмом и носителя не имела до `D39.231` ⚠ **ДОБАВКА 10.09 (замер движковой зоны по запросу оркестратора, подтверждён ключом БД).** Правка «применить `resolvedForResume` внутри `memberDrops`» ПОЛНА для этого пути (живой вызов один — `backend/internal/pipeline/export.go:307`=`drops := memberDrops(u, byChunk, draftStageNames)`) и попутно чинит артефакт порядка: внутренний цикл `status.go:538-543` оставляет ПОСЛЕДНЮЮ флагнутую черновую строку, а строки приходят `ORDER BY … stage`, поэтому при двух черновых стадиях `cancelled` может перебить контентную причину; фильтр возвращает контентную. Случай «сверху лёг `cancelled`» НЕВОЗМОЖЕН: строка апсертится по `(book_id, chapter, chunk_idx, stage)` (`backend/internal/store/chunkstatus.go:92`), а контентно флагнутая стадия не пере-выполняется (резюм-гейт `backend/internal/pipeline/stagerun.go:93`). ⛔ **НО правка меняет ЛОЖЬ на МОЛЧАНИЕ, и это не закрытие:** если единственный флаг члена — `cancelled`, после фильтра `DroppedMembers` = 0 и `DroppedReason` пуст (`export.go:320-322`), единица уходит `ok`, молча не досчитавшись текста члена. ⇒ закрывать надо ТРЕТЬИМ состоянием («единица неполна из-за остановки»), а не одним фильтром. Окно узкое: нужна комбинация «финальная строка от прежнего прогона + редрайв члена + стоп» (`export.go:174`) ⛔ **ВТОРАЯ ПОЛОВИНА, вытащенная 10.09 из отменённого пака, чтобы не умереть вместе с ним (ПОДОЗРЕНИЕ — выведено ЧТЕНИЕМ кода движковой сессией, фикстурой НЕ гонялось):** строка `chunk_status` не добавляется, а ЗАМЕЩАЕТСЯ — апсерт по ключу стадии с `DO UPDATE SET … final_hash = excluded.final_hash` (`backend/internal/store/chunkstatus.go`, греп `on conflict`), а запись отмены кладёт ПУСТОЙ `final_hash`. ⇒ прогон, который ПЕРЕ-атакует позицию с уже готовой строкой (редрайв · пере-пин с `--resnapshot` · правка исходника) и будет оборван, оставляет позицию с пустым хешем — выгрузка теряет текст, чекпойнт которого лежит на диске. То есть отмена способна стереть НАСТОЯЩИЙ вердикт, а не только пометить незаконченное. Проверяется фикстурой «готовая строка → редрайв → обрыв» ⚠ **ИСПР. 11.09 — ОДИН ИЗ ТРЁХ МАРШРУТОВ ОПРОВЕРГНУТ ЧТЕНИЕМ, и опровергла его АВТОР прежней формулировки:** через РЕДРАЙВ сценарий НЕДОСТИЖИМ. `ResetChunkStages` (`backend/internal/store/chunkstatus.go`, греп `func (s *Store) ResetChunkStages`) в ОДНОЙ транзакции удаляет и `checkpoints`, и `chunk_status` сбрасываемых стадий — пере-снято мной: две `DELETE` между `BeginTx` и `Commit`; то же говорит доккоммент `backend/internal/pipeline/volume.go` (греп `DELETES the unit`). ⇒ к моменту пере-атаки замещать нечего, прежнего вердикта уже нет. **Остаются ДВА маршрута**, оба обходят быстрый путь резюма при ЖИВОЙ прежней строке: пере-пин `--resnapshot` (снапшот уехал, признак пере-пинуемости ложен) и правка исходника или промта (не сходится хеш содержимого). Их и предъявляет фикстура; исход «недостижимо» — такой же результат ⚠ **РАСШИРЕН 16.09 актом `D39.255`: то же и для МЕТКИ СТОПА.** `memberDrops` считает выпавшим члена с ЛЮБОЙ флагованной черновой строкой и причину не спрашивает (пере-снято приёмкой на обоих деревьях: `cancelled` → 1, `retry_unaffordable` → 1, контроль `hard_refusal` → 1; предикат байт-идентичен HEAD). ⇒ читатель получает «ТРЕБУЕТ ПРОВЕРКИ ЧЕЛОВЕКОМ», а оператор — «missing for good … cannot bring those back» там, где следующий прогон с поднятым потолком доделает и члена, и эдит. Верная починка — третье состояние члена («не доделан, но доделается»); фильтр в `memberDrops` меняет ложь на молчание.| бэкенд | скоро | единица, неполная ИЗ-ЗА ОСТАНОВКИ, отличима и от выпавшего контента, и от полной: ни ложного контент-вердикта, ни молчания | седьмой круг ревью пака 360, 08.09; добавка — замер зоны 10.09 | | 376 | **ДОСТАВЛЕННЫЙ ОБРЫВ ДО ЗАГОЛОВКОВ КНИЖИТСЯ НУЛЁМ — и мы НЕ ЗНАЕМ, у скольких провайдеров это срабатывает.** Деньги за оборванный нами вызов считаются по `answered && afterHeaders` (`backend/internal/llm/attemptcut.go`), то есть единственный оплачиваемый обрыв — пришедший через ошибку ЧТЕНИЯ ТЕЛА. Обрыв до первого байта ответа (наш дедлайн, стоп родителя, разрыв сокета) даёт **$0** и не виден даже в оценочной паре (`paidtail.go` пропускает `CostUSD <= 0`). ⚠ **Сужение СОЗНАТЕЛЬНОЕ и ратифицировано** (`D39.230`, разведение предикатов): довод замером — 3 из 25 отменённых в полёте сеттлили оценку за запрос, которого не видел ни один хендлер, то есть переплата ЧИТАТЕЛЯ, запрещённая `D39.196` п.2а. ⛔ **Открыт РАЗМЕР остатка, и он неизвестен по построению:** провайдеров **8** (замер по `configs/models.yaml`), ранний `200` задокументирован ровно у ОДНОГО (DeepSeek), а про поведение заголовков у остальных СЕМИ в репозитории не сказано НИЧЕГО — греп класса по странице квирков даёт 0 при 250 строках файла. ⇒ верное утверждение не «сломано у семи», а **«неизвестно для семи»**: если заголовки у них приходят рано, путь почти не срабатывает; если поздно — головной класс пака у них не закрыт вовсе. ⚠ Замерить без платных вызовов нельзя, и постфактум из телеметрии тоже: движок не пишет время до первого байта успешных вызовов. ⇒ первый шаг дешёвый и $0 — начать писать это время | бэкенд | скоро | время до первого байта пишется; размер остатка назван числом, а не «неизвестно» | приёмка пака «вызов, который оборвали мы» 10.09 | | 377 | **ОПЛАЧЕННЫЙ ОБРЫВ, ЗА КОТОРЫМ РЕТРАЙ УСПЕЛ, ИСЧЕЗАЕТ БЕССЛЕДНО: $0 и ни одного следа.** `backend/internal/llm/httpllm.go` — на успешном ретрае `if err == nil { return resp, nil }`, и накопленный `owedCut` ВЫБРАСЫВАЕТСЯ. Замер приёмки: сервер спрошен ДВАЖДЫ (первый вызов доставлен, подтверждён и оборван), вызывающий получает чистый успех и о первом не узнаёт ничего — ни строки леджера, ни оценки, ни пометки. ⚠ Это ровно предмет пака (платим дважды за одну генерацию), но пак его НЕ заказывал: §4.2 требовал деньги «на исчерпании», а здесь исчерпания нет — ретрай удался. Ни одна из девяти фикстур этого пути не покрывает: `TestABrokenConnectionAfterDeliveryIsRetriedOnceAndOnlyOnce` роняет ОБЕ попытки | бэкенд | скоро | оплаченный обрыв виден в леджере независимо от исхода ретрая | приёмка пака «вызов, который оборвали мы» 10.09 | | 378 | **ПИН «НЕ-2XX — НЕ ПОКУПКА» ЛОВИТ МУТАНТА НЕ ТЕМ АССЕРТОМ: денежные проверки в нём не исполняются ни разу.** Каталожная мутация `CUTCALL-a-non-2xx-becomes-a-purchase` (батарейная, цель — `backend/internal/llm/httpllm.go`, ветвь статуса `if resp.StatusCode < 200 || resp.StatusCode >= 300` → `if false`). Пере-снято ОРКЕСТРАТОРОМ 10.09 на копии: тест краснеет, но ТЕКСТОМ `cutcall_test.go:584: a provider failing every attempt must surface as an infra failure` — ассертом об исходе ПРОГОНА. `t.Fatal` останавливает тест ДО трёх проверок, ради которых пин написан (`committed`, `reserved`, `checkpoints`), и они не исполняются. Под мутацией движок сеттлит **$0.001056 как ОЦЕНКУ за 500-ку** — ровно ту утечку, которую доккоммент теста называет, — и текст падения об этом молчит. ⚠ Класс ратифицирован (`D39.217` п.2в, `D39.232` п.6в) — здесь открыт НОСИТЕЛЬ | бэкенд | скоро | мутант краснеет ассертом О ДЕНЬГАХ: посылка «прогон упал» проверяется так, чтобы не глотать денежные проверки | приёмка пака «вызов, который оборвали мы» 10.09 (С8) + пере-замер оркестратора |