From 7af35daf965457b070c879ccfe7c468b3423f8b1 Mon Sep 17 00:00:00 2001 From: heaven Date: Fri, 11 Sep 2026 00:24:20 +0300 Subject: [PATCH] Move the closed chronicle of a landed pack into an archive slice, and give the three claims it carried alone a home that is not the archive. --- docs/BACKLOG.md | 7 +- docs/ORCHESTRATOR_SESSION_PROMPT.md | 9 + docs/PROGRESS.md | 604 +---------------- .../PROGRESS-2026-09-04-07-bank-law.md | 608 ++++++++++++++++++ 4 files changed, 629 insertions(+), 599 deletions(-) create mode 100644 docs/archive/PROGRESS-2026-09-04-07-bank-law.md diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 21ce8e1e..c2d294e5 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -3,7 +3,7 @@ > **Единственный трекер проекта.** Здесь живут строки, на которые доки, промты и D-ноты ссылаются словами «строка N» / «строка бэклога N»: **ID строки стабилен навсегда**, не перенумеровывается и не переиспользуется (D39.80). Правки — только через оркестратора; каждая петля обязана иметь диспозицию: решено / отложено-с-записью / отклонено. > ⚠ **Это НЕ бэклог зоны `docs/`, а бэклог ПРОЕКТА.** У зон свои, с другими неймспейсами ID, и единый их строк не принимает (D39.84): платформа — [../platform/BACKLOG.md](../platform/BACKLOG.md) (`П-N`) и её регистр дефектов `platform/docs/DEFECT_REGISTER.md` (`PD-N`); фронт — [../frontend/docs/BACKLOG.md](../frontend/docs/BACKLOG.md) (`Ф-N`). > ⚠ **Состояние проекта — не здесь.** Очередь, курс, CURRENT-STATE, состояние паков и живая хроника — шапка [PROGRESS.md](PROGRESS.md). Здесь только долг и его диспозиции. -> - **СЧЁТ ОЧЕРЕДИ на 10.09 (скриптом по таблице — `python3 docs/scripts/counts.py`; обновлять при каждом лендинге):** всего **287** строк · зона бэкенд **138** строго / **185** широко (⚠ колонки счётчик читает С КОНЦА — испр. D39.116) · **блокеров очереди 0** (236 закрыта D39.175; **371** — актом D39.233, **360** — актом D39.232, обе 10.09), платные прогоны разблокированы · «скоро» **136** (перечень — грепом по таблице, рукописный список снят D39.126) · гейт-строка эксп-22: 153 (плюс 150 — руки владельца; ⚠ 55 из гейтов СНЯТА — D39.198 п.5); строка 5 — остаток гейчен ре-пробой 74, носитель события теперь строка 188 (аудит D39.140); остальное «когда-нибудь». ⚠ Счёт — НИЖНЯЯ граница долга, не потолок (разбор — легенда таблицы ниже). +> - **СЧЁТ ОЧЕРЕДИ на 10.09 (скриптом по таблице — `python3 docs/scripts/counts.py`; обновлять при каждом лендинге):** всего **288** строк · зона бэкенд **138** строго / **185** широко (⚠ колонки счётчик читает С КОНЦА — испр. D39.116) · **блокеров очереди 0** (236 закрыта D39.175; **371** — актом D39.233, **360** — актом D39.232, обе 10.09), платные прогоны разблокированы · «скоро» **137** (перечень — грепом по таблице, рукописный список снят D39.126) · гейт-строка эксп-22: 153 (плюс 150 — руки владельца; ⚠ 55 из гейтов СНЯТА — D39.198 п.5); строка 5 — остаток гейчен ре-пробой 74, носитель события теперь строка 188 (аудит D39.140); остальное «когда-нибудь». ⚠ Счёт — НИЖНЯЯ граница долга, не потолок (разбор — легенда таблицы ниже). > - ⚠ **Переезд 06.09 (D39.218):** таблица вынесена из `PROGRESS.md` ДОСЛОВНО и ни один ID не сдвинут — **с единственным исключением: две ячейки (строки 220 и 306) тронуты РОВНО в номере якоря `docs/scripts/counts.py:566`→`:569` и `:592`→`:595`**, потому что цель сдвинул этот же коммит; текст ячеек не менялся, длины равны. В `PROGRESS.md` на прежнем месте оставлен заголовок-указатель, поэтому ссылки «секция „Бэклог“» доезжают в один хоп. ## Бэклог (ЕДИНЫЙ, собран 26.07, актуализация 04.08 D39.99/D39.101; правки — только через оркестратора) @@ -319,7 +319,8 @@ | 395 | **DEV И ПРОД РАСХОДЯТСЯ В ДВАДЦАТЬ РАЗ ПО ВРЕМЕНИ НА СВОРАЧИВАНИЕ, И ЭТО НИГДЕ НЕ ОБЪЯВЛЕНО.** `platform/internal/ingest/supervisor.go`, греп `WaitDelay` — тридцать секунд на dev-пути; `platform/internal/runner/runner.go`, греп `stopGrace` — десять минут на боевом. Один и тот же движок против тех же платных провайдеров получает на свёртку полминуты или десять минут в зависимости от пути запуска. Расхождение само по себе может быть законным, но оно НЕ НАЗВАНО, и dev-замер «успевает свернуться» ничего не говорит о боевом (и наоборот) | платформа | когда-нибудь | оба числа названы в одном месте с доводом, почему они разные, либо сведены | улов платформенной сессии, 10.09 | | 396 | ⛔ **ГЛАВНАЯ РАЗВИЛКА «ПРАВИЛЬНОГО ВОЗОБНОВЛЕНИЯ»: платформа ПЕРЕЗАПУСКАЕТ прогон, вышедший с кодом остановки БЕЗ записанного намерения.** `platform/internal/runs/reconcile.go`, греп `interruptedBySomeoneElse`. Решение осознанное и с записанным доводом (перезагрузка хоста против ручного `systemctl stop`, асимметрия цен), поэтому строка не о дефекте. ⚠ Но инвариант `D39.240` («правильно возобновить») обязан пересмотреть эту развилку ЯВНО, а не унаследовать: сегодня она означает, что прогон, остановленный мимо продуктовой двери, будет поднят заново и продолжит тратить. Названо платформенной сессией как то, что новый пак не должен взять по умолчанию | платформа | скоро | поведение при коде остановки без намерения ПЕРЕ-решено под инвариант корректного возобновления, а не унаследовано | улов платформенной сессии, 10.09 | | 397 | **ПЯТЬ ОТКРЫТЫХ РЯДОВ РЕЕСТРА ПЛАТФОРМЫ ПРО ХОЛДЫ И ОСТАНОВКУ — ОДИН ПРЕДМЕТ, А НЕ ПЯТЬ РАЗРОЗНЕННЫХ.** Их печатает собственный гейт зоны как тревогу. Прямо по предмету остановки и возобновления: **PD-418** (у расчётной строки живого прогона нет ручки, а рантбук обещает оператору обратное) · **PD-244** (единственный выход расчёта, оставляющий холд открытым МОЛЧА) · **PD-162** (книга с удалённым каталогом принимает прогон и заклинивает его навсегда с открытым холдом) · **PD-465** (заявка на спавн коммитится ДО подъёма юнита, третий способ уехать под доказательством не ловится) · **PD-217** (книга с незакрытым холдом блокирует апгрейд движка бессрочно). ⇒ под инвариантом `D39.240` это готовый предметный список «не оставить мусор», уже приоритизированный реестром зоны | платформа | скоро | пять рядов закрыты ОДНИМ предметом либо явно разведены как разные | улов платформенной сессии, 10.09 | -| 398 | ⛔ **ПРОГОН, ВЫШЕДШИЙ БЕЗ МАРКЕРА, ПЕРЕЗАПУСКАЕТСЯ БЕЗ ВСЯКОГО ОГРАНИЧЕНИЯ — капа попыток у платформы НЕТ.** Юнит кончился, маркера нет (сломан `ExecStopPost`, повторная перезагрузка) → после льготного окна спавна и без записанного намерения платформа считает состояние свежим и ПЕРЕЗАПУСКАЕТ: новый холд, новый спавн. На ДОЧИТАННОЙ книге движок выходит сразу и снова без маркера ⇒ цикл повторяется каждым проходом свипа. Замер: `MaxAttempts` в `platform/internal` вне тестов — **7 хитов, все в `jobs.go` и все про очередь**, ни одного про попытки прогона (контроль: слово `restart` в `platform/internal/runs/reconcile.go` есть и читается). ⚠ И тревога МОЛЧИТ: счётчик отсрочек очищается «уликой» каждого рестарта, поэтому порог зависания недостижим. ⚠ **Честная граница (Fable 5, 10.09): путь ЗАМЕРЕН чтением, сам бесконечный цикл ВЫВЕДЕН, прогоном не предъявлен** | платформа | скоро | у перезапуска без маркера есть потолок, а его исчерпание видно оператору | разбор старшего коллеги по инварианту `D39.240`, 10.09 | -| 399 | **ПЛАТЯЩИЙ ПРОГОН МОЛЧА ЗАМИРАЕТ В ИНТЕРФЕЙСЕ: проекция ПАРКУЕТСЯ при ре-спавне той же попытки.** Курсор встречает чужой хендшейк, проекция останавливается до конца попытки — `platform/internal/runs/reconcile.go`, греп `ErrForeignStreamAhead`; комментарий там же признаёт: «the "stranger" may be this very run, alive and writing». ⇒ прогон жив, платит и пишет, а прогресс не двигается. **У состояния «запаркована» нет носителя** — ни столбца, ни счётчика; репэйр-канал чинит оценку времени и свежесть, но не прогресс. Пользователь видит замерший экран без объяснения | платформа | скоро | у запаркованной проекции есть носитель состояния, и пользователю сказано, что прогресс отстал, а не остановился | разбор старшего коллеги, 10.09 | +| 398 | ⛔ **ПРОГОН, ВЫШЕДШИЙ БЕЗ МАРКЕРА, ПЕРЕЗАПУСКАЕТСЯ БЕЗ ВСЯКОГО ОГРАНИЧЕНИЯ — капа попыток у платформы НЕТ.** Юнит кончился, маркера нет (сломан `ExecStopPost`, повторная перезагрузка) → после льготного окна спавна и без записанного намерения платформа считает состояние свежим и ПЕРЕЗАПУСКАЕТ: новый холд, новый спавн. На ДОЧИТАННОЙ книге движок выходит сразу и снова без маркера ⇒ цикл повторяется каждым проходом свипа. Замер: `MaxAttempts` в `platform/internal` вне тестов — **7 хитов, все в `jobs.go` и все про очередь**, ни одного про попытки прогона (контроль: слово `restart` в `platform/internal/runs/reconcile.go` есть и читается). ⚠ И тревога МОЛЧИТ: счётчик отсрочек очищается «уликой» каждого рестарта, поэтому порог зависания недостижим. ⚠ **Честная граница (Fable 5, 10.09): путь ЗАМЕРЕН чтением, сам бесконечный цикл ВЫВЕДЕН, прогоном не предъявлен** ⭐ **УСИЛЕНО пере-замером платформенной сессии 11.09: естественного ограничителя в ДЕНЬГАХ нет.** Она искала его и не нашла: перезапуск сперва РАССЧИТЫВАЕТ старую попытку, потом берёт новый холд; попытка ничего не потратила (движок выходит сразу) ⇒ холд возвращается целиком и берётся равный новый ⇒ **цикл денежно-нейтрален на итерации и балансом не останавливается**. ⚠ Но цена НЕ нулевая: каждая итерация пишет строки леджера — расчёт и холд, — то есть цикл производит именно тот мусор, против которого заведён инвариант `D39.240`. ⭐ И материал для потолка уже есть: номер попытки инкрементируется каждым пере-открытием, значит капа выражается «номер больше N» БЕЗ новой колонки (доказательство спавна для этого не годится — оно на попытку, а перезапуск открывает новую) | платформа | скоро | у перезапуска без маркера есть потолок, а его исчерпание видно оператору | разбор старшего коллеги по инварианту `D39.240`, 10.09 | +| 399 | **ПЛАТЯЩИЙ ПРОГОН МОЛЧА ЗАМИРАЕТ У ПОКУПАТЕЛЯ, КОГДА ПРОЕКЦИЯ ПАРКУЕТСЯ. ⚠ Строка ПЕРЕ-ПИСАНА 11.09: первая редакция утверждала, что у состояния «запаркована» нет носителя — ЭТО БЫЛА МОЯ ОШИБКА, носителей ЧЕТЫРЕ**, и пере-снято их платформенной сессией: столбец `run_attempts.parked_at` (миграция `00034_spawn_proof_and_park.sql`) · писатель `Store.MarkParked` (`platform/internal/pgstore/runs.go`) · чтение на выписке (`LiveRun.Parked`) · **операторский гейдж** `parked_attempts` (`platform/internal/metrics/metrics.go`), плюс операторский листинг и лимитированная по частоте строка лога (`sayParked`). Если бы строка ушла в заказ как написана, пак пошёл бы строить построенное — ровно класс «условное закрытие», который мы вычищаем из доков. **⇒ ЖИВАЯ половина — ПОЛЬЗОВАТЕЛЬСКАЯ, и только она:** оператор видит парковку четырьмя способами, а ПОКУПАТЕЛЬ видит замерший экран без объяснения. Механизм: курсор встречает чужой хендшейк и проекция останавливается до конца попытки (`platform/internal/runs/reconcile.go`, греп `ErrForeignStreamAhead`), причём комментарий там сам признаёт: «the "stranger" may be this very run, alive and writing» | платформа | скоро | пользователю сказано, что прогресс отстал, а не остановился — тем же каналом, которым он видит прогресс | разбор старшего коллеги + пере-замер платформенной сессии, 10–11.09 | | 400 | **ПРАВКА БАНКА ЛЕГЛА, ПЛАТФОРМА УМЕРЛА ДО ЗАПИСИ О НЕЙ — СЛЕДУЮЩИЙ ПРОГОН ВОЗЬМЁТ ХОЛД И УМРЁТ НА ГАРДЕ.** `platform/internal/runs/bank.go`, греп `RecordBankMove`: ветка ошибки стора обработана (клиент пере-шлёт, сходится), а смерть ПРОЦЕССА между применением правки и записью факта канала не имеет. Цену называет собственный комментарий кода: отметка о движении банка остаётся пустой ⇒ следующий прогон допускается без пере-снапшота ⇒ умирает на снапшот-гарде уже ПОСЛЕ взятого холда (`PD-425`). ⚠ Прямо на предмете `D39.240`: «не оставить мусор» и «правильно возобновить» — это ровно оно | платформа | скоро | смерть между правкой банка и записью о ней не приводит к взятому холду и умершему прогону | разбор старшего коллеги, 10.09 | | 401 | **МУСОР НА ДИСКЕ ПОСЛЕ ОБРЫВА: три носителя, ни один не подметается.** **(а)** артефакт сборки при смерти платформы посреди неё остаётся на месте — перечень удаляемого берёт только строки с путём (`platform/internal/exports/exports.go`, греп `Unlinked`); **(б)** временные файлы движка чистит ТОЛЬКО следующая сборка того же идентификатора, которой при обрыве не будет; **(в)** частичный бэкап движка ложится под именем, которого платформа не знает (`platform/internal/runner/backup.go`). ⚠ Отдельно, инертно, но противоречит леджеру: отметка о расчёте может остаться пустой навсегда, если расчёт прошёл, а её запись — нет; читателей столбца вне тестов **0** (греп: только писатели и комментарии) | платформа | когда-нибудь | обрыв не оставляет на диске файлов, которых никто не подметёт, и отметка о расчёте не расходится с леджером | разбор старшего коллеги, 10.09 | +| 402 | **ТРИ УТВЕРЖДЕНИЯ, КОТОРЫЕ ВЫНОС ХРОНИКИ В АРХИВ ОСТАВИЛ БЫ БЕЗ НОСИТЕЛЯ — заведены ТЕМ ЖЕ движением, что и вынос** (условие ока старшего коллеги на дифф, `D39.239` п.4). **(а) Стейл-упоминания снятого маркера `⟨проверить⟩` ВНЕ движка** — вынесенный отчёт называл `platform/internal/runs/spawn.go`, `platform/internal/runs/reconcile_test.go`, `platform/docs/DEFECT_REGISTER.md` (PD-277) и `eval/pilot/memory_eval.py`; все места живы сегодня, плюс `eval/pilot/kana_precision.py`. ⚠ **Стейл ли они — НЕ доказано:** в движке маркер стоит в **10** не-тестовых носителях и 7 тестовых, то есть снят он, судя по числам, только с провода инъекции. Проверять и чинить — чужим зонам (платформа, полигон), пинги им отправлены. **(б) Следствие ФЧ-7: прогон, который УПАЛ поверх потолка, приезжает `paused` с пустым кодом выхода** — по терминам «упал поверх потолка / ФЧ-7 / exit_code nil» носителей **0** в журнале решений, едином бэклоге и регистре платформы (PD-113 — исходный класс, не это следствие). Код платформы принимает это как конструкцию (греп `survives ANY exit`), но выбор «краш невидим за `paused`» нигде не записан как ПРИНЯТЫЙ. **(в) Две границы «не проверено» заланденного пака, у которых нет носителя нигде:** качество рода не измерено на живой книге; цена банкового контура в `c1` на реальной книге не мерена | доки | скоро | каждое из трёх либо получило носитель в своей зоне, либо признано неверным и снято | условие ока на вынос хроники 11.09 | diff --git a/docs/ORCHESTRATOR_SESSION_PROMPT.md b/docs/ORCHESTRATOR_SESSION_PROMPT.md index 064239fc..fad97914 100644 --- a/docs/ORCHESTRATOR_SESSION_PROMPT.md +++ b/docs/ORCHESTRATOR_SESSION_PROMPT.md @@ -240,6 +240,15 @@ D23.3). Владельцу на решение: деньги сверх мело от него. Настоящие развилки сессий эскалируй быстро и решай сам; сессии, приносящие развилки вместо тихих девиаций, — поощряй. +### Вынос блока в архив: якорь НИЖЕ разреза уезжает вместе с ним + +⛔ Проверять надо не «есть ли якоря В ВЫРЕЗАЕМОМ диапазоне», а **«есть ли якоря с номером ≥ начала разреза»**: +всё, что стояло ниже, сдвигается на длину вырезанного и становится битым молча. Замер 11.09 при выносе +196 КБ из живого журнала: якорей вида `PROGRESS.md:NNN` во всём живом дереве — **25**, максимальный номер +**1190**, начало разреза — 3009 ⇒ класс не сработал только потому, что все двадцать пять оказались выше. +⚠ И греп по `docs/` для этого мало: канон велит НЕ исключать `archive/architecture/` — там тела закрытых +эр журнала решений, и они тоже ссылаются. + ### Приписывание — проверяется у ИСТОЧНИКА, а не пересказывается ⛔ **Находка, пришедшая через третьего, не заводится строкой с именем ПЕРВОГО, пока первый её не подтвердил.** diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 0f327090..70c9eba4 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -3006,602 +3006,14 @@ polygon, `mined_delta`/`mined_rejects` — в 17 конфигах из 31, по --- -## Пункт 1 — строка 210: род ПРОИЗВОДИТСЯ. ЗАКРЫТ, с честной границей доставки - -**Развилка выбрана ЧЕТВЁРТАЯ, которой в промте не было: род производит существующий фокус-проход КЛАССИФИКАТОРА** (`runClassifier`), третьим полем ответа. Довод: ноль новых вызовов (проход уже гоняется по тем же кандидатам с теми же контекстами) при цели №5 «низкий COGS»; род — свойство сущности, выводимое из контекстов, то есть акт классификатора, а не выбор рендеринга; парсер классификатора позиционный и закрыто-словарный, тогда как третье поле терминолога — уверенность, вокруг которой написана деликатная развязка мис-сплита по живому дефекту «наставник 95%». Цена: двигается `RequestHash` ТОЛЬКО классификаторных вызовов; терминологические чекпоинты книг живы. Ратифицировано оркестратором. - -**Построено.** Промпт пары `prompts/zh-ru/classifier.md` v2 — третье поле, закрытый словарь `male|female|neuter|none`. ⚠ `hidden` в словарь НЕ входит НАМЕРЕННО: «пол скрыт до раскрытия» — решение о сюжете и спойлерной политике книги, а не наблюдение по KWIC-строкам; ошибочная догадка де-гендерила бы обычного персонажа на всю книгу. Остаётся ручным, в сиде. `none` — полноценный ОТВЕТ, а не пропуск колонки: без слова для «рода нет» единственный способ это сказать — не писать колонку, что неотличимо от «модель перестала отвечать». -Go: `terminology.Genders` + `ParseTypes` возвращает `types, genders, stats`; `ReplyStats.BadGender`/`NoGender` — два счётчика, потому что это два разных отказа; `pipeline.attachClassifiedGender`; `miner.Term.Gender` → `DeltaFile` → сид-YAML → сайдкары → банк. -**Гарантия, которую держит тест:** колонка рода НИКОГДА не стоит классу — значение вне словаря отбрасывается и считается, класс сохраняется (`TestTheGenderColumnNeverCostsTheClass`). - -**⛔ ГЛАВНАЯ НАХОДКА ПАКА: ЗАМКОВ ТРИ, И НАЗВАННЫЙ В ПРОМТЕ — НЕ СВЯЗЫВАЮЩИЙ.** -`bankmaterialize.go` держит ДВА банка: черновая волна выбирает по `baseMemory`, из которого строки `Source:"mined"` вычеркнуты ПО ПОСТРОЕНИЮ, редактор видит обогащённый. Авто-банк пишется как `mined` (`mining.go:671`), подписанная дельта — тоже (`mining.go:896`, дословно «override the seed loader's Source:seed → mined (base-excluded)»). ⇒ **на ЧЕРНОВОЙ провод произведённый род не едет и не поедет: там нет самой СТРОКИ.** Это не дефект — это денежный контракт `D39.42` п.3: инъекция черновой волны байт-стабильна поперёк обогащения банка, иначе пере-снапшот платит две волны вместо одной. Не трогал и не предлагаю. -**Формулируй границу так и не короче:** род производится авто-путём и доезжает до РЕДАКТОРСКОГО провода в том же прогоне, что его произвёл; на ЧЕРНОВОЙ не едет, пока цена двух волн не решена отдельно. Не «род построен». - -**Второй замок СНЯТ по ратификации оркестратора, и снят РАСЩЕПЛЕНИЕМ, а не целиком.** `Disp` сворачивал два вопроса — СТАТУС строки и надёжность МАТЧА. `D39.104` п.1 упраздняет разнобой по статусу («банк на проводе = закон независимо от статуса строки»), но про матч там нет ни слова и быть не может: род, приписанный не той сущности, — ошибка, а не неподписанность. Заведено поле `PickedEntry.KeyTrusted` + тип `InjectionTrust{Disp, KeyTrusted}`, который несёт ОБА факта через липкое окно (у липкого переноса нет своего ключа, и матч-половину, как и диспозицию, восстановить нечем). Род теперь гейтится `KeyTrusted` на ОБОИХ блоках; статус не решает ничего. -**Сквозное доказательство — три двери порознь, а не «работает»** (`internal/pipeline/gender_wire_test.go`): (1) неподписанная строка на доверенном ключе НЕСЁТ род редактору в том же прогоне; (2) collision-prone матч не несёт НИКУДА, при ЛЮБОМ статусе (юнит-проверка: на zh ветка не срабатывает физически, ханьские ключи якорятся на 2, поэтому сквозной zh-фикстурой её достать нельзя); (3) на черновой провод род не попадает НИКОГДА — и утверждение перевёрнуто нарочно: откроет кто-то дверь 3 — тест покраснеет и заставит автора назвать, какую именно. -**Отрицательный контроль есть:** `TestASignedGenderBeatsTheProducedOne` — сид владельца побеждает, и не порядком присваивания, а тем, что `loadAutoBank` роняет предложение движка на занятый подписью ключ. -Команда: `go test ./internal/pipeline/ -run 'Gender' -v`. - -⚠ **Пункт 4 — ПРЕДПОСЫЛКА пункта 1 в бою.** Род производится только когда включён `classify_types`. Без банкового контура в боевом конфиге род не производится ни при каком варианте развилки. - -## Пункт 2 — строки 113 → 46: база записана, экран построен. ЗАКРЫТ - -**113 — СТОП СНЯТ, все три порога пройдены.** Корпус взят исполнением: все чекпоинты всех 162 БД стенда, через СОБСТВЕННУЮ предобработку движка (`splitBanknote` → `checks.StripThink` — тот текст, который видит гейт), дедуп по хешу, только отгрузочные роли (банк-роли исключены: их ответы законно состоят из исходных термов, и включив их, мерили бы исключение, а не гейт). Обход дал **1162 ответа: 30 off-target, 1132 здоровых** — но ⚠ **в РЕПОЗИТОРИЙ уехала ВЫБОРКА, и все числа ниже воспроизводимы над ней, а не над обходом**: 74 строки, все 30 off-target плюс 44 ЗДОРОВЫХ, отобранных нарочно трудными (20 с высшей долей исходного письма, 15 с низшей долей целевого, 3 под полом улики, остальное — спред). Тексты в git не поехали, довод — в шапке `offtarget_corpus_test.go`. -**Разметка НЕ гейтом, иначе она была бы пересказом вердикта:** off-target = доля КИРИЛЛИЧЕСКИХ букв < 0.5 — свойство ЦЕЛЕВОЙ стороны, независимое от проверяемого исходно-письменного гейта. Корпус разделяется полностью: здоровые все выше 0.9, off-target все ровно 0.0000, полоса между ними ПУСТА. -**Числа воспроизведены и совпали с опубликованными до знака:** худший здоровый source-share **0.011655** (контракт называл 0.0117), запас **12.87×** против порога 0.15 (контракт называл 13×), **ноль ложных на 44 трудных здоровых** (не на 1132 — над репозиторием проверяемо именно это), поймано **28 из 30** (контракт называл 18 из 20 на более узком срезе того же стенда — та же доля, те же два промаха). Промахи — два беглых АНГЛИЙСКИХ черновика на **4690** и **4811** букв, ровно те, что названы в `EFFORT_HANDLE §3.1`. Худшая здоровая доля ЦЕЛЕВОГО письма **0.9767**, максимум у off-target **0.0000**, самый короткий off-target **346** букв — все три числа контракта воспроизведены. -Команда: `go test ./internal/pipeline/ -run 'OffTargetCorpus|LiveEchoGate|LatinOffTarget' -v`. - -⚠ **ЧЕГО ФИКСТУРА НЕ НЕСЁТ, и это мой выбор с ценой.** `testdata/offtarget-corpus.json` несёт РУНОСЧЁТ каждого образца (всего рун, рун исходного письма, букв, букв целевого письма), происхождение, метку и шестидесятирунную голову для человека — но не тексты. Тексты — копирайтная вебновелла и её машинные переводы, которые репозиторий держит вне git намеренно (стенд версионируется отдельным репо, `example/` возит собственный фрагмент по той же причине). Руносчёта достаточно ПО ПОСТРОЕНИЮ: обе проверяемые функции — отношения классов рун и ничего больше не читают, и тесты кормят НАСТОЯЩИЕ функции скрипт-верным суррогатом, собранным из счётчиков, а не считают арифметику вместо них. **Названная потеря:** детектор, который будет читать слова, n-граммы или орфографию, на этой фикстуре не измеряется — ему нужен повторный обход стенда. - -**46 — экран построен формой (а) плана §5.4 и заморозкой `D39.92`/`D39.93`.** Отдельный пакет чистых функций `internal/langscreen` (вход: текст + скрипты цели; выход: ИМЕНОВАННЫЙ вердикт `on_target|off_target|abstain` + счёт и объём улики). Порог `OnTargetFloor = 0.50` — КОНСТАНТА в пустом интервале, не тюнинг (гарантия Неймана–Пирсона на этом корпусе не покупается — `D39.92` п.1а); пол улики `MinLetters = 200` калиброван, а не выбран: самый короткий off-target 346 букв, ниже пола падают три здоровых. Носитель данных — `lang.LangScripts`, НОВОГО эмбед-файла не заведено. -**Флаг СВОЙ — `FlagOffTargetLang`, `FlagCJKArtifact` НЕ переиспользован** (`D39.93` п.2), явный вход в `escalatable()`, не `retryable`. `classifierVersion` v2 → v3, и версия правила `langscreen.Version` вплетена в литерал, так что правка ВНУТРИ модуля двигает снапшот сама, а не по дисциплине. -**Приёмочный критерий §5.4 шага 5 исполнен:** офлайн-прогон по корпусу — экран новооткрывает РОВНО два ответа, оба английские, здоровых не двигает ни одного (в тесте это `Fatal`, а не `Error`: план требует СТОП). Вдвоём два правила ловят **30/30 при нуле ложных**. -Команды: `go test ./internal/langscreen/ -v` · `go test ./internal/pipeline/ -run 'TargetScreen|TwoScreens|BankRoleReply' -v`. - -**Вторая половина дыры закрыта тем же диффом:** `detectLatinInsertion` теперь под `c.TargetScriptNonLatin()` (`internal/checks/sanitizer.go`). Правило существовало в прозе доккоммента и стояло только на пути репэйра; первая же латинописьменная цель уронила бы КАЖДЫЙ чанк чистой целевой прозы как «Latin insertion». Для ru вердикты не двигаются ни на байт (предикат истинен) ⇒ бампа версии санитайзера не требуется. - -⚠ **НАХОДКА, приехавшая ВМЕСТЕ с паком, а не до него.** Экран и эхо-гейт обязаны молчать на ответах банк-ролей: их буквы — движковые идентификаторы и исходные термы, поэтому эхо-гейт флагал бы их (и флагал: `verdict=cjk_artifact` при `bad_lines=0` на чистой пробе 6/6), а экран флагал бы ВТОРЫМ способом. Исключение было записано как `role == roleTerminologist` и КЛАССИФИКАТОРА не покрывало (`13-tech-debt-anchors §Б-105`) — латентно, пока ни один боевой конфиг классификатор не включал. Пункт 4 его включает. Поле переименовано в `NonProseReply` (оно про ФОРМУ ответа), предикат один — `isBankRole`, рядом со строкой, которую новая банк-роль и так обязана добавить в `bankRoleSettings`. - -## Пункт 3 — строка 134: рендер снят, данные не тронуты. ЗАКРЫТ - -**Выбор объявлен: снят РЕНДЕР в Go.** Механизм, по которому выбор сделан: `cutTag` сворачивает `lang.EmbeddedVersion()`, `injection.txt` лежит в `internal/lang/data/` ⇒ правка данных пере-чеканила бы `unit.id` всей книги → `manifest_key` → снос `unit_resolutions` у платформы (строка 198). `RenderFormatVersion` в `cutTag` НЕ входит — он фолдится только в снапшот. Предъявление — первой строкой этого отчёта. -**Текст заголовка не менялся вовсе,** и это проверено, а не принято на веру: действующий `editor_header` БАЙТ-ИДЕНТИЧЕН арму `AM-noclause` пробы 18 (`experiments/18-editor-wire-probe.md` §0.3 дословно: «Закон БЕЗ оговорки = действующий `editor_header` без единой правки»). Оговорка области НЕ внесена (опровергнута §A.4). -**Что изменилось:** маркер ⟨проверить⟩ не рендерится НИ В ОДНОМ блоке; секция `editor_unverified_header` не рендерится, две секции редактора схлопнуты в один закон-блок; неподписанные строки стоят в нём под тем же заголовком и ничем не помечены. Статусы строк и подписная таблица не тронуты (`D39.104` п.1). `RenderFormatVersion` v3 → v4. -**Ревью-ось «маркер снят, а пост-чек жив» проверена:** `membank.Postcheck` ключуется на `p.Disp`, маркера не читает — обработка `Ambiguous` цела, счётчики `Shown`/`Followed` живы. ⚠ Но их СМЫСЛ сузился, и это записано в код: они родились на проводе, который помечал строку и приглашал модель её отвергнуть; теперь неподписанная строка показана законом, отклонение — просто отклонение, а не «модель пользуется дарованным правом». Та же поправка внесена в доккоммент миграции v12, которая эти колонки завела. -**Операторские тексты, которые стали бы ложью, переписаны** (не комментарии — то, что печатается человеку): две строки `tmctl` про «уедет к редактору с пометкой ⟨проверить⟩», строка `Unsigned bank terms` в `report`, WARN стопа майнинга, WARN конфликта с подписанным банком. - -⚠ **НАХОДКА, которую я НЕ ЧИНИЛ и которая теперь висит на строке 250** (носитель заведён оркестратором): строка данных `ru glossary_header` САМА ОПИСЫВАЕТ снятый маркер («строки с пометкой ⟨проверить⟩ — неподтверждённые кандидаты»). После пака помеченных строк на проводе нет, а заголовок продолжает объяснять модели пометку. Это не мёртвая строка данных (`editor_unverified_header` и `unverified_marker` — те просто перестают читаться, и обе оставлены ОБЪЯВЛЕННЫМИ полями, потому что их СТРОКИ остаются в эмбеде, а `parseInjection` отвергает незнакомый ключ), а ЖИВАЯ строка с висячей ссылкой. Чинится только правкой `injection.txt`, то есть ценой `cutTag`. По `D39.104` п.1 клаузула становится ВАКУУМНОЙ, а не ложной; вред не измерен. - -## Пункт 4 — строка 140: контур внесён в c1. ЗАКРЫТ с НАЗВАННЫМ предусловием развёртывания - -**Внесено в `configs/pipeline-c1.yaml`:** `gates.banknote.enabled: true` + `prompt_override: ../prompts/zh-ru/translator-banknote.md` на стадии draft со своим лейблом (`D39.10`: включение банкноты — ДВА согласованных изменения; сам гейт только СРЕЗАЕТ блок, инструкцию писать его несёт промпт) · `gates.terminology` (модель `deepseek-v4-flash`, `reasoning: low` — у рендер-фазы это вынужденно, на вендорском дефолте она сжигает бюджет размышлением и возвращает пустое тело) · `classify_types: true` · `mining.contrast_path`. - -**Суб-бюджеты — числа и их довод.** Множитель ×4.47 чинит пере-пин цен DeepSeek и ТОЛЬКО его; прежние 0.05/0.02 размерены под десятиглавую пробу, а этот файл обслуживает книги целиком. Замер (строка 16, десять глав, июльские деньги ×4.47): терминолог **$0.0257**, классификатор **$0.0344**. Бюджеты КНИГО-ШИРОКИЕ и копятся по всем прогонам книги (`store.RoleSpentUSD` суммирует чекпоинты роли по всем её джобам), а кандидатное пространство терминолога зависит от того, какие чанки уже отчерновлены, — значит между покупками оно меняется, `request_hash` другой, и книга, проданная десятью покупками, платит десять книжных консолидаций. Пол ⇒ **0.257 / 0.344**. Взято **1.00 / 1.00**, запас **3.9× / 2.9×**. -⚠ **Первая редакция дала классификатору 0.50, то есть запас 1.45×, а комментарий рядом обещал «≈3×» — число одной величины под именем другой. Поймано МОИМ ЖЕ адверсариальным проходом, исправлено: по замеру классификатор ДОРОЖЕ рендера, и «половина» пришла из старых чисел, а не из измерения.** Бюджеты остаются раздельными при равных значениях: разделение про то, чтобы перерасход одной фазы не голодал другую. -Пин: `TestTheBankRoleBudgetsClearTheirMeasuredCost` — это ПОЛ, а не значение; поднимать можно свободно, опускать ниже замеренного нельзя. - -**⛔ ПРЕДУСЛОВИЕ РАЗВЁРТЫВАНИЯ, названное вслух: `mining.contrast_path` — единственный вход контура, которого нет в git.** ОБА банк-роля живут ВНУТРИ стопа майнинга, и `runBankMiningStop` возвращается РАНЬШЕ построения хоть одного кандидата без корпуса контраста. Артефакт (jieba-словарь общей частотности, 5 МБ) под `.gitignore` намеренно, но ВОСПРОИЗВОДИМ — рецепт и sha256 запинены в `docs/experiments/16-bank-mining.md`. Носитель требования к рантбуку и слово оператора — строка **251** (заведена оркестратором). - -**Два гарда, которые делают «настроено и мертво» невозможным** — и оба нашлись не по заказу, а по разбору: -1. Загрузка отказывает, если гейт включён, а `mining.contrast_path` ПУСТ (`internal/config/pipeline.go`) — тот же класс «гейт, который не может выстрелить», который лоадер ловит везде. -2. Наличие ФАЙЛА проверяется на ПИШУЩЕМ пути, рядом с `CheckKeys` (`Pipeline.CheckMiningContrast`, вызов в `openRunner` внутри `forWrite`). Место выбрано не случайно: `os.Open` внутри прогона стоит ПОСЛЕ оплаченной черновой волны, то есть развёртывание, забывшее артефакт, платило бы за волну, чтобы это узнать. $0-поверхности не трогаются (`D20.4`). -3. ⚠ **ТРЕТИЙ гард добавлен по находке адверсариального прохода, и он закрывает дыру в моих же первых двух.** У раннего возврата ДВА условия, и я гейтил только одно: `r.pack == nil` — а нил-пак книга получает, если не объявила `langpack_root` вовсе или объявила корень без каталога своей пары («nil-and-run»). Такая книга могла нести полностью настроенный и забюджетированный контур и не исполнить из него ни строки. Конфиг-лоадер этого структурно не видит: гейт знает пайплайн, пак знает книга, и вместе они есть только в раннере. Отказ добавлен там, тоже только на пишущем пути. - -## Пункт 5 — строка 184(а): жанр выкинут. ЗАКРЫТ - -`grep -rl '{{genre}}' backend/prompts/ | wc -l` → **6 → 0**. Поле снято из канона брифа (`BriefHash`), `{{genre}}` снят из плейсхолдеров `pipeline.Render` — значит промпт-пакет пары, написанный позже с этим маркером, падает ГРОМКО на первом рендере, а не подставляет пустоту. Репо-гейт на ту же вещь: `TestNoShippingPromptAsksForTheGenre`. -**`BriefHash` двинулся у ВСЕХ книг** (`c2021b1b…` → `4624529b…` на голден-фикстуре) — это заказано `D39.138` п.2в и внесено в общее окно пере-снапшота именно потому, что платится один раз со всем батчем. -⚠ **Ключ `genre:` ОСТАВЛЕН ПРИНИМАЕМЫМ, и это не мягкость.** Платформа пишет книге `book.yaml` ОДИН раз из шаблона оператора и никогда не перезаписывает, протаскивая незнакомые ключи насквозь — `genre` там назван явно (`platform/deploy/README.md`). Декодер движка строгий (`KnownFields`), поэтому удаление поля отказало бы в загрузке книгам, которые уже лежат на диске, посреди живого прогона соседней сессии. Идиома — та же, что у ретированного `adult: false`. Значение не читается ничем: `TestARetiredGenreKeyStillLoadsAndReachesNothing` держит и загрузку, и равенство `BriefHash` у двух книг, отличающихся только жанром. -⚠ Лейблы промптов бампнуты и леджер пере-записан (`v1-reflow` → `v2-reflow-nogenre`, `v3-discourse-reflow` → `v4-discourse-reflow-nogenre`): байты промптов уехали, а лейбл обязан следовать за ними — иначе два прогона под одним лейблом несравнимы, что этот гейт и ловит. **Тем же касанием в свип леджера добавлен КЛАССИФИКАТОР:** он был вне его, пока ни один боевой конфиг его не включал, а его промпт решает КЛАСС термина, то есть маршрутизирует имя в транскрипцию, а реалию в перевод. -⚠ `genre: ранобэ` НАМЕРЕННО оставлен в `testdata/golden/book.yaml` с комментарием «не прибирать»: это место, где терпимость к ретированному ключу упражняется на каждом захвате. - -## Пункт 6 — дофикс чужого пака + ФЧ-7. ЗАКРЫТ - -⚠ **Это остаток ЧУЖОГО наряда (`D39.187` п.4), а не мой заказ** — сессия, делавшая тот пак, не пережила рестарт окружения. - -**(а) Развилка операторской строки прибита со ВТОРОЙ стороны.** Воспроизвёл сам: ДВЕ посадки в новый код `VolumeStop.String()` переживали всё дерево (проверено с `GOFLAGS=-buildvcs=false`, иначе `cmd/tmctl` падает на VCS-штампе копии и маскирует результат) — (1) развилка заголовка всегда истинна, (2) развилка заголовка спрошена про ПЕРЕНОС вместо ОСТАТКА. Обе печатают в смешанном состоянии «reached the END of the book … Still in the book: 1 unit(s) NEVER delivered». Прибито `TestTheStopLineSaysTheGrantHeldWorkBackWhenItDid` — таблицей из ДВУХ строк, потому что две потери теряются на разных состояниях: всегда-истинная падает на любой, а спрошенная-про-перенос только там, где перенос и остаток оба ненулевые. Записи каталога `WB1`, `WB2`. -**(б) Граница `bound()` прибита.** `Carried > 0` → `Carried > 1` переживало: существующий тест переносит ДВА юнита. Прогон с ОДНИМ перенесённым на конце книги не отдавал отчёт вовсе — тихий лимит, анти-паттерн `D39.165` §1б. `TestOneCarriedUnitIsStillWorkOutsideTheGrant`, запись `WB3`. -**(в) Три числа отчёта — ПРОВЕРЕНЫ И СХОДЯТСЯ на HEAD.** `D39.187` п.4(в) говорит «протухли снова»; пере-снял все три их собственными командами до своей работы: батарея `EXIT=0` с теми же тремя скипами · `^func Test` **1105** · каталог **92**. Отчёт 03.09 называет 1105 и 92. ⇒ пункт (в) закрыт лендингом `f8d3e9a`, и остатка по нему нет. - -**ФЧ-7 — ВОСПРОИЗВЕДЁН и ПОЧИНЕН. Починка СВЕРХ заказа, объявляю.** Промт заказывал проверку исполнением и починку только при воспроизведении. -Первая попытка воспроизведения ПРОВАЛИЛАСЬ и это важно: после потолка фидер `runWave` перестаёт раздавать элементы, поэтому второй воркер паниковать не успевает. Нужна синхронизация, чтобы обе позиции были в полёте, — и тогда воспроизводится ровно как описано: `runWave` отдавал ТОЛЬКО панику, `errors.As(err, &CeilingHalt)` → false, `errors.Is(err, errReserveCeiling)` → false, `terminal()` падал в `failed` и события `ceiling` НЕ писал. -Почему это дефект, а не размен: `platform/internal/runs/reconcile.go` читает событие потолка ПЕРВЫМ и дословно говорит «that survives ANY exit: the process may have been killed on its way out, and the fact does not become less true». `PD-113` закрыт ДВУМЯ независимо отказывающими каналами; одна паника забирала оба. -Починка узкая: `runWave` присоединяет к панике ТОЛЬКО `*CeilingHalt` — не `context.Canceled` (проверено посадкой: присоединение отмены роняет `TestWaveWorkerPanicSurfacesAsAFailingError`, и правильно, крах не должен читаться как вызванный им же graceful stop); `terminal()` проверяет панику ПЕРЕД потолком, пишет событие `ceiling` и исход `failed`. Код выхода 1 и зеркало «исход = код выхода» не двинуты. Пины: `TestACrashOverACaughtCeilingKeepsBothFacts`, `TestACrashOverACaughtCeilingStillAnnouncesTheCeiling`, `TestACrashJoinedWithACeilingStillExitsAsACrash`. - -**ПЕРЕ-СКОУП строки 197 — как заказано.** ФЧ-1/4/5 закрыты паком «число согласия» (`D39.187`). **ФЧ-6 сверен кодом и выглядит закрытым**: отказ проекции репортится базисом, отчёт не валится (`internal/pipeline/status.go`, греп `RebillBasisFailed`). **ФЧ-7 закрыт этим паком.** **ФЧ-2, ФЧ-3 и ФЧ-8 НЕ ПРОВЕРЕНЫ НИКЕМ и остаются живым остатком строки** — см. секцию «Что НЕ проверено». - ---- - -## Свобода и её разметка — отчёт ПО КАЖДОЙ строке таблицы промта - -**«Делай РОВНО так» — исполнено всё, отступлений нет:** порядок 113 → 46 соблюдён (база записана ДО стройки экрана) · СТОП по фикстуре не сработал, все три порога пройдены с запасом · текст заголовка не менялся · статусы строк и подписная таблица не тронуты · нота бита спеку у `FlagCJKArtifact` — флаг СВОЙ · суб-бюджеты ×4.47 как ПОЛ, взято выше с объявленным доводом · жанр снят и из промптов, и из канона брифа · обе посадки дофикса краснеют · ФЧ-7 проверен исполнением (и починен сверх заказа — объявлено) · все запреты «Границ» соблюдены. -**«Решаешь сам и аргументируешь» — по каждому пункту решение и довод выше:** развилка производства рода (§1) · второй замок — ВЫНЕСЕН пингом и ратифицирован оркестратором расщеплением, сам не снимал (§1) · данные в 134 — НЕ тронуты, объявлено первой строкой (§3) · числа суб-бюджетов (§4) · форма фикстуры 113 (§2, с названной ценой) · форма тестов и записей каталога · порядок работ (6 → 5 → 3 → 1 → 4 → 2, голден один раз в конце). -**Границы:** `manifestVersion`, `StreamVersion`, схема хранилища и версия контракта НЕ двинуты (предъявлено `tmctl manifest`) · новый эмбед-файл не заведён · `FlagCJKArtifact` не переиспользован · семантика переноса слота не тронута · строки 131 · 105(а) · 14 · 119 · 124 · 115 · 160 · 161 · 142 не брались · чужие зоны, `.env` и соседний worktree не трогались. - -## Самопроверка исполнением - -**1. Дифф `^func Test` ПОФАМИЛЬНО.** `grep -rn '^func Test' backend --include=*_test.go | wc -l` → **1105 → 1143**, добавлено 39. **Удалённых имён ОДНО:** `TestEditorSectionFollowsProvenance` — переименование, разбор в шапке. -**2. Голден пере-захвачен ОДИН раз, маскированный дифф разобран построчно.** 22 строки провода + 252 версионных. **Диспозиций, флагов и `chunk_status` не двинулось ни одного** (`diff` по этим строкам пуст; 252 строки до и после). -⚠ **Поправка к моей же формулировке, найденная адверсариальным проходом:** сказать «финальные тексты не изменились» было БЫ неверно. `final_text` изменился на всех 8 chunk-строках и `stage_text` на 22 — но ровно на ТОКЕН ХЕША, который фейк-провайдер эхом вкладывает в свой ответ, то есть следствием сдвига запроса, а не сменой вердикта. Точная формулировка: **вердикты не двинулись; каждый финальный текст отличается только эхом хеша запроса.** -⚠ **Вторая поправка того же прохода:** в ГОЛДЕНЕ двинулись `brief_hash`, `classifier_version`, `render_format_version` и `prompt_sha256` ЧЕРНОВОЙ стадии — а `memory_version` и `prompt_version` НЕ двинулись (у фикстуры свой лейбл `v-golden`, а токен фолда неподписанных строк заморожен намеренно). Мой первоначальный перечень осей был шире фактического. `memory_version` двигается на книге с гендерными строками, а не в этой фикстуре. -**3. Собственные посадки ВНЕ шести пунктов — 8, механизмом `cmd/tmmutate` на копии дерева.** 6 красных, **2 ПЕРЕЖИЛИ** и НЕ чинены (вне заказа, носитель — строка **252**): `StickyDepth = 2 → 1` не ловит никто · `priorityRank`, отбрасывающий приоритет доверия в бюджете инъекции, не ловит никто. Третья пережившая — `cutTag` без `lang.EmbeddedVersion()` — ПОЧИНЕНА по своей инициативе, потому что на этом компоненте стоит весь довод пункта 3 (`TestTheCutTagActuallyCarriesTheEngineData`, `TestEveryCutInputMovesTheTag`, запись `WB4`; `cutTag` разложен на `cutInputs`/`tag()` — рефактор байт-нейтрален, предъявлено сборками из HEAD и из дерева). -⚠ Методическая заметка, стоившая ложного результата: `tmmutate` на копии дерева НЕ МОЖЕТ судить тесты `cmd/tmctl`, которые собирают бинарь, — копия не git-репозиторий и сборка падает на VCS-штампе, маскируя всё остальное. Гонять копию с `GOFLAGS=-buildvcs=false`. -**4. Отдельный проход по СВОЕЙ ГОТОВОЙ работе свежим контекстом — ДВА агента, модель `fable`, оба read-only,** с заданием ФАЛЬСИФИЦИРОВАТЬ шесть заявлений каждый (деньги и провод · вердикты и данные). Ноль не декларирую: они принесли шесть находок, и четыре я починил. - -**Находки собственного адверсариального прохода — что нашли и что с этим стало:** -1. ⛔ **`bank-apply` СТИРАЛ произведённый род — то есть ровно тот акт, ради которого стоп существует.** `termFromBank` копировал только `Type` и `Aliases`; строка дельты уходила с `gender: ""`, а `loadAutoBank` потом ронял авто-строку как занятый ключ. ПОЧИНЕНО: род переносится вперёд, как переносятся алиасы (это находка движка ОБ СУЩНОСТИ, а решение о РЕНДЕРИНГЕ не просили её выбрасывать); дверь по-прежнему род не ПРИНИМАЕТ. Пин `TestApprovingATermKeepsTheGenderTheEngineProduced`, проверен красным на копии без правки. -2. ⛔ **Мой первый гард пункта 4 закрывал только одно из ДВУХ условий раннего возврата.** Второе — `r.pack == nil`, а нил-пак книга получает без `langpack_root` или с корнем без каталога своей пары. Такая книга несла бы настроенный и забюджетированный контур и не исполнила бы ни строки. ПОЧИНЕНО третьим гардом в раннере (пишущий путь), где гейт и пак есть вместе — конфиг-лоадер этого структурно не видит. -3. ⛔ **`repairReGate` звал `classify` БЕЗ `TargetScripts`** — экран цели на пути ремонта воздерживался, судя везде: починка, вернувшаяся на другом языке, прошла бы ре-гейт, вся работа которого в том, чтобы спросить «это всё ещё пригодный ответ». ПОЧИНЕНО: ре-гейт зовёт тот же классификатор с теми же входами. -4. ⛔ **`classify_budget_usd: 0.50` давал запас 1.45×, а комментарий рядом обещал «≈3×».** ПОЧИНЕНО: 1.00, запас 2.9×, и названо, что по замеру классификатор ДОРОЖЕ рендера. -5. ⛔ **Тест `TestTheLiveEchoGateKeepsItsMeasuredMargin` проходил на фикстуре с ПУСТЫМ off-target классом:** `0/0` = NaN, а любое сравнение с NaN ложно. ПОЧИНЕНО знаменателем-предусловием. Тем же касанием: суррогат целевой стороны привязан к записанному счёту (как уже был исходный), добавлены проверка `target_script_letters ≤ letters` и `TestTheScreenConstantsStillSitWhereTheCorpusPutThem` — константы `langscreen` сверяются с ДАННЫМИ, а не с константами (порог обязан лежать в пустом интервале корпуса, пол улики — не выше самого короткого off-target). -6. ⚠ **`allowshort_wire_test.go` утверждал ОТСУТСТВИЕ маркера — после пункта 3 это утверждение не может упасть.** Заменено на то, что фикстура ещё способна держать: `allow_short` доставляет строку ЦЕЛИКОМ, вместе с её родовой директивой. -Плюс поправлены четыре комментария, ставшие ложью: `decisions.go` («род — сид-онли без автопроизводителя»), доккоммент `langscreen` про →en, `BriefHash` про исторический хеш, и три стейл-упоминания маркера в тестах зоны. - -## Что НЕ удалось и что НЕ проверено - -⚠ Исходов ТРИ, и «не проверено» ≠ «опровергнуто» ≠ «сделано». - -**НЕ ПРОВЕРЕНО (никем, включая меня):** -- **ФЧ-2, ФЧ-3, ФЧ-8 строки 197.** Их я не открывал вовсе. ФЧ-2 — два докстринга `reprice.go` описывают не то, что делает код (детекция перебора односторонняя; «остаток несётся по счёту» неверно). ФЧ-3 — снятый закон approved-only продолжает утверждаться в докстринге теста и в `backend/docs/D15.2-*.md:317`. ФЧ-8 — докстринг `TranslateBook` про «каждый выход через `terminal()`» неверен для паники вне волны · комментарий `dispatchCommands` · шапка рига. Живой остаток строки 197. -- **Ничего в этом паке не проверено ПЛАТНЫМ вызовом.** Ни одна модель не вызывалась: санкции на деньги у меня не было и я её не просил. Всё измерено на персистированных чекпоинтах стенда и на фейк-провайдерах. -- **Качество рода не измерено.** Что классификатор реально отвечает в третьей колонке на живой книге — не мерено ни разу. Промпт написан консервативно (`none` объявлен самым частым и правильным ответом при нехватке контекстов), но верно ли он определяет пол — вопрос замера, а не стройки. ⚠ Это ПЕРВЫЙ кандидат в следующий полигон-пак: ось рода теперь производит данные, и ложный `male` поедет консистентно через всю книгу — то самое свойство, ради которого консистентность и строилась. -- **Экран цели не проверен на живом провале.** Он ловит два образца, которые уже лежат в стенде; поведёт ли он себя так же на СВЕЖЕМ off-target событии — не мерено. Порог 0.50 стоит в пустом интервале и не калиброван (и это по `D39.92` п.1а и не может быть иначе на этом корпусе). -- **Цена банкового контура в c1 на реальной книге не мерена.** Числа суб-бюджетов — проекция от десятиглавого замера, не наблюдение. Первый же боевой прогон с контуром даст настоящие числа, и их надо сверить с полом. -- **Наличие корпуса контраста на БОЕВОЙ машине.** Я его не видел и видеть не мог. Строка 251. - -**НЕ СДЕЛАНО СОЗНАТЕЛЬНО (с доводом):** -- **Замок №0 не трогал** — базовый банк исключает `Source:"mined"` по денежному контракту `D39.42` п.3. Следствие названо в §1: род не едет на ЧЕРНОВОЙ провод и не поедет, пока цена двух волн не решена отдельно. Не моё решение. -- **`ru glossary_header` не тронут** — висячая ссылка на снятый маркер, чинится только правкой `injection.txt`, то есть ценой `cutTag`. Носитель — строка 250. -- **Две пережившие мутации не чинены** — вне заказа, носитель 252. -- **Строка 204 / `PD-246`: карту причин платформы не правил** — чужая зона. Пинг отправлен напрямую (`textmachine-main-34`), она пере-сняла счёт своей командой и завела у себя; состав контрактного минора несёт оркестратору, потому что изобретать код замечания платформенная сессия не вправе. - -**НАЙДЕНО И НЕ ЧИНЕНО — ЧУЖИЕ ЗОНЫ (передаю фактом, не правкой):** -- ⚠ **Следствие починки ФЧ-7 на стороне платформы, которого я не предвидел.** `pgstore/sink.go` кладёт событие `ceiling` в `paused_reason`, а `runs/reconcile.go:1008` возвращает `paused` с `exit_code = nil`. ⇒ прогон, который УПАЛ поверх потолка, теперь приезжает `paused` и КОД ВЫХОДА теряется — крах становится невидим на строке прогона. Это лучше прежнего для пользователя (ему говорят «добавь денег», и это правда и действие), но хуже для операций. Взвесить и решить — не мне; несу как факт. -- ⚠ **Стейл-упоминания снятого маркера вне моей зоны:** `platform/internal/runs/spawn.go:164` (прод-комментарий), `platform/internal/runs/reconcile_test.go:723`, `platform/docs/DEFECT_REGISTER.md:412` (PD-277), `eval/pilot/memory_eval.py:440-452` (заявляет «byte-synced with Go glossaryBlockHeader» и по-прежнему дописывает маркер построчно — зеркало провода в полигоне разъехалось). -- ⚠ **`platform/deploy/` ничего не знает о корпусе контраста** (`grep -rn 'contrast_path\|mining:' platform/deploy/` → пусто). Половина строки 251. -- ⚠ **Дыра покрытия, найденная платной сессией и переданная мне** (`PD-441`, её ряд): вызов, отменённый В ПОЛЁТЕ по книжному потолку, уходит веткой «No 2xx ever arrived: nothing was billed» → `releaseReservation` и нулевая цена в `request_log`, хотя замер 04.09 показывает отмены на 120756/156469/156470 мс — то есть запрос был выпущен и, скорее всего, оплачен. Латентность разводит два случая (20–31 мс против 156 с). **Я это НЕ чинил: `stagerun.go` в моей зоне, но это ДЕНЕЖНЫЙ путь и отдельный предмет, а не пункт вайр-батча.** Заказ на него — отдельным паком. - -**ЧЕГО Я НЕ ДЕЛАЛ ПО ГРАНИЦАМ:** не коммитил, индекс и историю не трогал; `platform/` и `eval/` только читал; соседний worktree не открывал; `.env` не читал. - -## ДОФИКС ПО ПРИЁМКЕ (04.09) — четыре пункта наряда, все закрыты - -⚠ **Класс, который приёмка вскрыла и которого моя самопроверка не нашла: СЕМЬ ЗАЯВЛЕННЫХ ГАРАНТИЙ СУЩЕСТВОВАЛИ ТОЛЬКО КАК КОД.** Двенадцать её посадок пережили полную батарею. Это ровно образец `D39.187` — тот же дефект, что этот пак чинил у чужого пака, повторённый мной. И нашёлся он там, где я сам сказал «ставь гуще», то есть карта слепых зон работает; чего у меня не было — это привычки сажать мутации на СВОЙ новый код, а не только на соседний. - -**Д1 — пять неприбитых гарантий прибиты, каждая тестом И записью каталога (`WB5`–`WB13`, каталог 96 → 105, все красные одним прогоном).** -· **F2, тяжелейшая: живая доставка строки 46 не была прибита вовсе** — `grep -rn 'FlagOffTargetLang' --include=*_test.go` давал ПУСТО. Модуль экрана был прибит офлайн (по руносчёту фикстуры) и по своему пакету, а вердикт, ради которого он построен, на живом пути не проверялся ни разу: переживали и «экран всегда воздерживается» (`Screen(out, nil)`), и «`chunkrun` не передаёт `TargetScripts`», и **снятие флага из `escalatable()` — прямое требование `D39.93` п.2, без которого пойманный чанк молча ТЕРЯЕТСЯ вместо пере-атаки**. Прибито: `TestAnOffTargetCompletionIsFlaggedAndEscalated` (вердикт + деталь + `escalatable` + `!retryable` + эхо остаётся эхом), `TestTheScreenIsInertWithoutTargetData`, `TestTheRunnerHandsTheScreenItsData` (через `classifyOutput` — ЕДИНСТВЕННЫЙ живой call-site, плюс исключение обеих банк-ролей). -· **F10: порог экрана не был ИСПОЛЬЗОВАН ни в одном тесте** — случая с долей между границами не было, то есть константа могла быть любой. `TestTheOffTargetThresholdIsUsed`: 0.60 → ok, 0.40 → флаг. -· **F1: три гарда контура** — `TestABankContourThatCanNeverFireIsRefused` (лоадер судит КЛЮЧ и не судит ФАЙЛ) и `TestTheContourRefusesAWriteRunItCannotExecute` (обе половины раннего возврата: отсутствующий артефакт и нил-пак; с ПРЕДПОСЫЛКОЙ, что до порчи фикстура открывается, и с обязательной проверкой, что `$0`-чтение той же книги ПО-ПРЕЖНЕМУ работает — `D20.4`). -· **F5: гард `TargetScriptNonLatin` в санитайзере** — `TestTheLatinResidueClassIsGatedOnTheTargetsOwnScript`, обе стороны. ⚠ Названная узость: `target-en.txt` в репо нет, поэтому «латинописьменная цель» достижима только через ПУСТОЕ объявление (та же ветка `wordScript == nil`); строку с настоящим `word_script latin` тест получит в день, когда такая цель появится. -· **F6: исключение банк-ролей** прибито было только юнитом на `isBankRole`; регресс до «терминолог-only» оставлял батарею зелёной. Теперь ловится через `classifyOutput` (`WB7`). - -**Д2 — $0-валидация плейсхолдеров ДО первого вызова. ДЕНЬГИ.** -Приёмка подтвердила исполнением то, что я принёс и не починил, сочтя вопрос про громкость: отказ на `{{genre}}` приходил ПОСЛЕ купленной черновой волны (`stage=draft cost_usd=0.001820`, `edit wave started`, затем отказ; `drafts=1 edits=0`). **Вопрос был про МОМЕНТ, и я его не расслышал.** Построено: `CheckPlaceholders` сканирует шаблон по тому же ЗАКРЫТОМУ набору, что подставляет `Render` — набор вынесен в одно определение (`renderVals`), потому что два списка это и есть способ, которым валидатор благословляет имя, которое рендер отвергнет. Вызов стоит в `LoadPromptTemplate` — единственной воронке, через которую проходят ВСЕ шаблоны: стадии, классы репэйра и обе банк-роли. Живой источник таких пакетов настоящий: `grep -rl '{{genre}}' eval/ | wc -l` → **27**. -⚠ **Заказанная смена поведения, объявляю (D39.183):** `TestRenderRejectsUnknownPlaceholder` описывал отказ ОТ `Render`; отказ переехал на загрузку. Тест не ослаблен — он теперь держит ОБА рубежа (гейт на загрузке И собственный отказ `Render`), плюс что закрытый набор проходит целиком, плюс что отказ НАЗЫВАЕТ и маркер, и то, что движок принимает. - -**Д3 — закон-блок даёт ОДИН приказ на исходный термин. Ратификация исполнена ровно так.** -Дедуп переведён с `(src,dst)` на `src`. Довод, который я принимаю целиком и не смягчаю: пока у блока было два заголовка, две передачи одного термина были двумя РАЗНЫМИ высказываниями — «это канон» и «это неподтверждённый рабочий вариант»; **мой же пункт 3 схлопнул их в два ПРИКАЗА об одном термине**, а с разными родовыми директивами — в два противоречивых приказа об одном персонаже. Побеждает первая строка, и «первая» тут не случайность: `Select` отдаёт в приоритетном порядке, `priorityRank` ставит `Confirmed` раньше `Ambiguous` — то есть подписанная. Конфликт не спрятан, а РАЗВЕДЁН: движок уже пишет о нём WARN оператору (`seeding.go`), и это правильное деление — **модель получает один закон, оператор получает разногласие**. Отбрасывание строки на загрузке не бралось: это онтология банка, а нужна была узкая правка провода. Пин `TestTheLawBlockGivesOneOrderPerSourceTerm` кормит неподписанную строку ПЕРВОЙ (дедуп, хранящий первую строку ВХОДА вместо первой строки приоритета, прошёл бы фикстуру в естественном порядке) и отдельно держит, что ГОМОНИМИЧНЫЙ dst — два разных исходных термина — остаётся двумя строками. - -**Д4 — числа отчёта поправлены.** F7: «ноль ложных на 1132» заменено на воспроизводимое над репозиторием — **ноль ложных на 44 ТРУДНЫХ здоровых**, с явным разделением «обход дал 1162/1132» и «в git уехала выборка 74 строк», и с описанием, как выборка отобрана. Это ровно класс «число одной величины под именем другой», который я чинил в пункте 4 и повторил в своём же отчёте. F8: расхождение шапки и секции самопроверки (1133/29 против 1135/31) снято — обе теперь **1143**, добавлено 39. - -⚠ **F11 — РИСК ДИЗАЙНА, не чинен, называю (вход в калибровку).** На латино-тяжёлом ЗДОРОВОМ русском экран цели даёт ложные `off_target`: русский с пиньинь-глоссами → 0.461, инструкции с shell-командами → 0.077. Ложный флаг ЭСКАЛИРУЕТСЯ, то есть стоит платного вызова. Корпус, на котором порог поставлен, — ОДНА сянься-новелла; жанр с системными окнами, кодом или плотной латинской терминологией не мерен вовсе. **Перед первым платным прогоном книги другого жанра порог обязан быть пере-калиброван на её корпусе.** Это не опровержение экрана (на измеренном жанре ноль ложных на трудных), это граница его калибровки. - -**Батарея после дофикса:** `MAKE_EXIT=0`, `0 issues`, 19 пакетов `ok`, скипы те же три. **Голден пере-захвачен — 0 изменений** (дофикс не двинул ни одного байта провода в фикстуре). Тесты **1105 → 1143**, удалённых имён по-прежнему одно и то же переименование. Каталог **92 → 105**, записи `WB1`–`WB13` красные. - -## КРУГ 2 ПЕТЛИ (`D39.191`) — НЕ ЗАМКНУЛСЯ: веер нашёл 21 пережившую посадку, все закрыты. Круг 3 обязателен - -⚠ **Условие замыкания жёсткое, и оно не выполнено:** круг замыкается, только когда проход с НЕПУСТЫМ веером не нашёл ни одной пережившей посадки. Этот нашёл двадцать одну. Ниже — веер, все находки с диспозициями, что я перепроверила сама, и чего круг не покрыл. - -### Веер -**Три источника, разные ракурсы.** (1) Агент `fable`, read-only — ДОФИКС, которого свежий контекст не видел: `CheckPlaceholders`, дедуп по `src`, новые тесты гардов и экрана на ВАКУУМНОСТЬ. (2) Агент `fable`, read-only — НЕПОКРЫТОЕ, ровно по моему же списку слепых зон: `volume.go` (пункт 6, чужой дофикс), закон-блок на книге с НЕПОДПИСАННЫМИ строками, бласт-радиус `InjectionTrust`, свип леджера, мелкие поверхности. (3) **МОИ собственные посадки — В ДАННЫЕ**, чего в круге 1 не было вовсе: фикстура корпуса, `prompt-labels.json`, `pipeline-c1.yaml`, промпт классификатора, `injection.txt`. -**Посадок всего: 12 моих + ~26 агента A + ~60 агента B.** Мои: 11 красных, 1 пережила. - -### Что нашлось — двадцать одна пережившая, по классам - -**А. ЖИВОЙ ДЕФЕКТ, внесённый моим же дофиксом (1).** -· **Дедуп закон-блока по голому `src` ронял ВТОРУЮ ПОДПИСАННУЮ строку.** Ключ уникальности банка — `(src, sense, since, until)`, то есть одна поверхность законно несёт несколько подписанных канонов: 青山-фамилия и 青山-направление. Мой дедуп выбрасывал один из них МОЛЧА, при том что трансляторный блок продолжал рендерить оба — два провода разъезжались. Правило исправлено на то, чем оно должно было быть с самого начала: **уступает НЕПОДПИСАННАЯ строка, и только подписанной; подпись не роняется никогда.** Пин красный на возврате к старому ключу. - -**Б. ДЕНЬГИ (2).** -· **`ReachableModels` не знал банк-ролей.** `buildClients` пре-строит именно этот список, `CheckKeys` требует именно эти ключи — оба на ОТКРЫТИИ. Модель терминолога или классификатора, не заведённая в `models.yaml` или без ключа в окружении, проходила ВЕСЬ преflight и убивала прогон на стопе майнинга — после купленной черновой волны. Латентно, пока контур не жил в боевом конфиге; пункт 4 делает это живым. -· **Банкнотный канал покупает вывод, который на этой конфигурации никто не прочитает** — единственный читатель за ранним возвратом. Не отказ (прогон корректен, срез делает свою работу), а предупреждение с ценой. - -**В. УТВЕРЖДЕНИЯ О СТРУКТУРЕ, ВЫДАННЫЕ ЗА ГАРАНТИИ (4) — класс, которого я в круге 1 не назвала.** -· «валидатор и рендер читают одну таблицу» — прибито только ПРИМЕРАМИ; валидатор-надмножество проходил всё; -· «проверка стоит в единственной воронке и покрывает все пути» — **все четыре пути загрузки промптов обводятся вокруг гейта, батарея зелёная.** Моя же формулировка из отчёта по Д2; -· «порог лежит в пустом интервале» — порог 0.50 → 0.45 переживает всё И НЕ ДВИГАЕТ ВЕРСИЮ: резюмнутый чанк пере-судился бы по порогу, за который никто не платил. ⇒ `langscreen.Version` теперь ВЫВОДИТСЯ из констант, `classifierVersion` стал var, правка порога роняет голден по построению; -· «свип леджера покрывает обе банк-роли» — свип ходил по НАЙДЕННОМУ и никогда по ЗАПИСАННОМУ, поэтому удаление роли из свипа оставляло её запись сиротой, а промпт — неприбитым. ⇒ проверка сирот. - -**Г. ДАННЫЕ (1) — и это то, ради чего норма про посадки в данные написана.** -· **Фикстура корпуса, размягчённая до красивого числа, проходила всё.** Тест утверждает пол «не меньше 3×», а пол не измерение: обнуление исходных рун у ЕДИНСТВЕННОГО худшего здорового образца оставляло батарею зелёной и рапортовало запас ЛУЧШЕ, чем корпус когда-либо давал. ⇒ леджер записанных чисел (30/44 строк · 0.011655 · 0.976706 · 346 букв), по образцу леджера лейблов: пере-взять корпус законно, но это стоит правки здесь и фразы в отчёте, а не ничего. - -**Д. РАСЩЕПЛЕНИЕ `Disp`, прибитое только там, где две половины СОВПАДАЮТ (3).** -· липкий перенос, ПЕРЕ-ВЫЧИСЛЯЮЩИЙ матч-доверие, переживал всё: единственный пин использует строку, чей `src` И ЕСТЬ сработавший ключ, поэтому пере-вычисление совпадает с наследованием. Разводит их строка с ханьским `src` и коротким фонетическим АЛИАСОМ — там перенос re-trusted, и род едет к сущности, которой на странице может не быть; -· липкий перенос, ВЫВОДЯЩИЙ доверие из диспозиции, тоже переживал: неподписанная строка на чистом ключе теряет род в момент переноса — дверь строки 210 в липкой форме; -· диспозиционная ось `priorityRank` не отличима от статусной ни одной фикстурой. Это одна из ДВУХ, переживших круг 1, и теперь на ней стоит мой дедуп. ⇒ прибита состоянием, которое оси разводит: две ПОДПИСАННЫЕ строки, одна на collision-prone ключе, под связывающим бюджетом. - -**Е. ЧУЖОЙ ПАК (пункт 6), которого свежими глазами не видел никто (6).** Из двадцати посадок в `volume.go` шесть пережили, и все шесть — в ОПЕРАТОРСКОМ ПРЕДЛОЖЕНИИ и в двух неотфикстуренных ветках `reconcile`: счёт «%d paying output unit(s)» без флагнутых (недо-счёт того, за что заплачено) · клауза про $0-попутчиков · развилка головы, спрошенная про `LeftFresh` вместо `Left()` · свободный юнит, флагнувшийся на резюме, посчитанный оплаченным · пере-деланный юнит, флагнувшийся и оставшийся В ОБОИХ счётчиках · и **порядок `bound()` ДО `reconcile`, от которого корректность отчёта зависит СЛУЧАЙНО**. Прибиты пятью тестами; последний — документирован в `waverun.go` с прямым указанием, какой «естественный рефактор» его сломает. - -**Ж. Прочее (4):** липкие записи, исчезающие из закон-блока · глубина липкого окна · сироты леджера · `injection.txt` без родовой строки (последнее — уже красное, поймано `TestDC3GenderInjection`). - -### Что я перепроверила сама, а что взяла со слов -**Перепроверила посадкой ДО починки (все, кроме одной):** дедуп по `src` · порог экрана · валидатор-надмножество · обход воронки на пути классификатора · диспозиционная ось · липкий перенос (обе формы) · сирота леджера (обе формы) · липкая запись в закон-блоке · банкнотный канал · размягчённая фикстура. -**Взяла со слов:** `H2` (два ряда с одним id — недостижимо через стор), `H7` (битый артефакт контраста молча даёт пустой контраст), и стейл-текст `glossary_header` — про него я уже знала. -⚠ **И одна собственная ошибка, которую поймал каталог, а не я:** первая редакция теста про `ReachableModels` читала `pipeline-c1.yaml`, где модель банк-роли СОВПАДАЕТ с моделью черновика, — то есть тест не мог упасть. Посадка сказала `SURVIVED`, я переписала тест на конфиг, где банк-модель своя. **Механизм проаудировал мой пин; чтение бы не проаудировало.** - -### Чего круг НЕ покрыл -- **Пункт 5 (жанр) и цепь рода end-to-end** — ни один агент их не атаковал: я дала им дофикс и непокрытое, считая эти два закрытыми кругом 1. Остаются проверенными одним проходом. -- **`$0`-проекции на дефектной книге** — `status`/`report`/`export`, реально отрендеренные: и мой тест, и посадки доказывают только, что КОНСТРУКТОР открывается. -- **Липкий перенос до РЕДАКТОРСКОГО провода сквозным прогоном** — ни одна фикстура не гонит прономинальный чанк с переносом в edit-юнит; всё прибито на уровне `membank`. -- **Настоящая латинописьменная цель** — `target-en.txt` в репо нет; ветка достигается пустым объявлением, и тест это говорит. -- **`eval/`-харнессы** — вне модуля бэкенда, не гонялись. -- **Сверка двух агентов между собой** — снова не делалась. - -### Числа круга -Батарея `MAKE_EXIT=0`, `0 issues`, 19 пакетов, скипы те же три. **Голден БАЙТ-ИДЕНТИЧЕН** тому, что был до починок круга (единственное движение голдена за круг — `classifier_version` от вывода версии экрана из констант; вердикты не двинулись). Тесты **1105 → 1156**. Каталог **92 → 120**, записи `WB1`–`WB29` красные одним прогоном. Удалённых имён по-прежнему одно — переименование `TestEditorSectionFollowsProvenance`. - -## КРУГ 3 ПЕТЛИ (`D39.191`) — ЗАМКНУЛСЯ ПО СОДЕРЖАНИЮ, НО НЕ ПО УСЛОВИЮ: веер нашёл 20 переживших посадок и 1 дыру исполнением, все закрыты - -⚠ **Условие замыкания не выполнено и здесь.** Круг замыкается, только когда проход с непустым веером не нашёл НИ ОДНОЙ пережившей посадки. Этот нашёл двадцать — плюс дыру, найденную не посадкой, а исполнением. Ниже веер, все находки с диспозициями, что я перепроверила сама, и чего круг не покрыл. - -### Веер -**Два источника, и это первый круг, где второй — не мой агент.** (1) Агент `fable`, read-only, работавший в СВОИХ копиях дерева (рабочее дерево не тронуто — подтверждено им и сверено по `git status`): **68 посадок в семи предметах**, 48 красных, 20 переживших, 0 несостоявшихся. (2) **Круг оркестратора** — независимый веер по тому же батчу, девять находок, пять новых для меня. По трём пунктам два веера сошлись НЕЗАВИСИМО (дедуп полисемии · порог без пина · класс структурных утверждений), и это первое за три круга свидетельство, что находки воспроизводимы между читателями, а не являются вкусом одного. - -### ⛔ ГЛАВНОЕ: денежная дыра, которую круг 2 ОБЪЯВИЛ ЗАКРЫТОЙ, была открыта - -Мой фикс круга 2 внёс банк-роли в `ReachableModels()` и на этом остановился. **`CheckKeys` читает этот список ТОЛЬКО в лейбл-ветке.** Безлейбловая книга — shipping-дефолт — идёт второй веткой, которая собирала модели стадий руками и про ворота не знала. Проверено исполнением: терминолог, классификатор и ремонт на провайдере с пустым `api_key_env` проходили префлайт целиком, `CheckKeys` возвращал nil. Прогон покупал черновую волну и умирал на воротах. - -**Почему тест круга 2 был зелёным поверх дыры:** он утверждал СПИСОК, а не префлайт. Пин на списке не есть пин на проверке, которая его читает. - -**И вторая половина той же дыры:** рантайм-список `reachableModels()` тоже не был прибит — посадка «только стадии» (без ремонта и банк-ролей) держала ВЕСЬ пакет `pipeline` зелёным. Причина — совпадение `pipeline-c1`, воспроизведённое в фикстурах: каждая фикстура ставит воротам `model: fake-model`, ту же модель, что у стадий. **Закрыто:** одно определение `gateModels()`, которое читают обе ветки `CheckKeys` и `ReachableModels`; тест зовёт `CheckKeys`, а не смотрит список; и фикстура, где банк-роль ходит СОБСТВЕННОЙ моделью, с пином на пре-строенной карте клиентов. - -### Что нашлось ещё — по классам - -**А. ЦЕЛЬ С CJK-ПИСЬМОМ НЕ ЭКРАНИРОВАЛАСЬ ВООБЩЕ** (находка оркестратора). Гейт `!isCJKTarget` принадлежал ЭХО-правилу, а держал оба правила сразу. «Вывод в письме ИСТОЧНИКА» для →ja бессмысленно — верный перевод zh-книги полон ханьцзы; «вывод в письме ЦЕЛИ» отвечается прекрасно. Итог: →ja/→zh/→ko не судились ничем, при том что доккоммент `langscreen` обещал обратное. **Это бьёт в цель проекта №2:** на ревью-вопрос «заработает ли пара, которой в репо нет, без правки Go» ответ для CJK-целей был «нет». Экран вынесен из-под гейта, эхо-правило осталось под ним. Документированный предел супермножества (китайский вывод для →ja не флагуется) прибит **как ПРОМАХ**, чтобы никто не принял экран за защиту от zh-эха на →ja. - -⚠ **`classifierVersion` НЕ двигали — ратифицировано оркестратором.** Для →ru поток управления побайтово тот же (`!isCJKTarget` истинно и до, и после, обе ветки в том же порядке), →CJK-снапшотов не существует, а строка в этом батче уже сдвинулась дважды и пере-снапшот уже предписан единым актом. Различитель на будущее: **бамп должен любой смене, способной пере-вердиктить СУЩЕСТВУЮЩИЙ снапшот** — не «кажется, безопасно». - -**Б. ПРОИЗВОДНОСТЬ ВЕРСИИ ЭКРАНА БЫЛА ЗАЯВЛЕНА И НЕ ДЕРЖАЛАСЬ (3 посадки).** Доккоммент `Version` говорит «выведена по построению, поэтому сдвиг порога забирает громкий `--resnapshot` с собой». Не проверял никто. Посадка: переписать `var` литералом, который он сейчас печатает, и сдвинуть порог 0.50 → 0.45 — строка не двинулась, голден не двинулся, оба интервальных пина прошли (0.45 внутри пустой полосы), весь пакет зелёный. Резюмированный чанк пере-вердиктился бы порогом, за который никто не платил. Третья дверь: `%.2f` не выражает 0.504 — окно точности ±0.005. **Закрыто:** тест пересчитывает строку из констант, и round-trip-guard требует, чтобы напечатанный порог читался обратно тем же числом. Формат НЕ расширен намеренно — это стоило бы `--resnapshot` всем книгам за правило, которое не менялось; расширение теперь осознанный акт. - -**В. ГРАНИЦА СРАВНЕНИЯ, КОТОРУЮ НЕ ВЫРАЖАЕТ НИ ОДНА КОНСТАНТА.** `share < floor` → `<=` пережило весь пакет: ни одна фикстура не сидела ровно на линии. Прибито фикстурой ровно на 200 буквах, из которых ровно 100 целевых — доля 0.50 без плавающего люфта. - -**Г. ВАЛИДАТОР ПЛЕЙСХОЛДЕРОВ — ТРИ ДВЕРИ.** (1) Сканирование только System-части пережило всё — каждая фикстура кладёт плохой маркер в системную половину, а самое вероятное место для автора — пользовательский хвост, где живут почанковые переменные. (2) Снятие проверки незакрытого `{{` пережило. (3) Тест «равенства множеств» обходит `placeholderNames()`, то есть таблицу РЕНДЕРЕРА, и доказывает только «рендерер ⊆ валидатор»; дорогое направление — обратное, и оно было прибито четырьмя ЛИТЕРАЛЬНЫМИ словами, так что пятое слово проходило насквозь. **Закрыто устранением, а не пином: валидатор ТЕПЕРЬ ЕСТЬ РЕНДЕРЕР** — `CheckPlaceholders` рендерит по настоящей таблице значений. Второго списка не существует, расходиться нечему. - -**Д. ЗАКОН-БЛОК: ПОДПИСЬ ПРОТИВ УВЕРЕННОСТИ (5 посадок).** Подпись — это СТАТУС, а не уверенность матча: подписанная строка, сработавшая по сомнительному ключу, всё равно канон, которому уступают. Ключевание набора подписей на диспозиции пережило. Уступать должен ЛЮБОЙ неподписанный статус, а не только `draft` — `auto` пишет майнер, то есть ровно та строка, ради подчинения которой правило и существует. Плюс: подписанная строка с ПУСТЫМ переводом не канон (иначе термин исчезает из блока), несимметричный `TrimSpace` разрушает набор, и однопроходная версия зависит от порядка. **Отдельно — вакуумность в моём собственном тесте:** его комментарий утверждал, что случай (1) держит независимость от порядка, а стоящий там же `sortByPriority` это утверждение обнулял. Комментарий исправлен, независимость от порядка вынесена в отдельный случай, который рендерит блок НАПРЯМУЮ, в обоих порядках. - -**Е. ЛИПКИЙ ПЕРЕНОС: ПОЛОВИНА ДИСПОЗИЦИИ.** Перенос наследует обе половины, но пересчёт диспозиции ИЗ СОБСТВЕННОЙ ПОВЕРХНОСТИ строки пережил: в прибитой фикстуре `src` сам и есть сомнительный ключ, поэтому пересчёт воспроизводит тот же ответ. Разделяющая фикстура (ханьский `src`, сомнительный кана-алиас) существовала, но утверждала только `KeyTrusted`. Под посадкой перенос молча повышался до `Confirmed` — пропуск принудительного пост-чека и место повыше в бюджете, — а провод молчал, потому что половина доверия цела. Это D16.2-повышение в чужом пальто. - -**Ж. ЛЕДЖЕР КОРПУСА ПИНИЛ ЭКСТРЕМУМ, А НЕ ПОПУЛЯЦИЮ (5 посадок).** Второй по трудности здоровый образец можно было смягчить, второй по низости целевой — тоже, а трудную строку — заменить числами лёгкой ПОД ЕЁ ЖЕ id: счётчики, экстремумы и множество id держались. И у интервала не было записи про ВТОРУЮ сторону: доля off-target могла уехать 0.000 → 0.490 при всех зелёных тестах, и порог 0.50 сел бы прямо на образец, пока леджер говорит «полоса пуста». **Закрыто:** запись `wantBestOffTargetTgt` и **дайджест по всей популяции** (id · метка · четыре счётчика, сортировка по id) — пере-взятие корпуса стало осознанным актом, переписывающим строку в том же коммите. - -**З. ОПЕРАТОРСКОЕ ПРЕДЛОЖЕНИЕ: ЧИСЛО ФЛАГОВ ПЕЧАТАЛОСЬ ЧИСЛОМ ПЕРЕДЕЛОК.** Пережило весь пакет: обе фикстуры, читающие это предложение, имели `Reworked == Flagged == 1`. Разведены. - -**И. ФЛАГ, РАДИ КОТОРОГО ПОСТРОЕН ПУНКТ 2, БЫЛ САМЫМ БЕЗОБИДНЫМ В ПАСПОРТЕ** (находка оркестратора). `flagReasonSeverity` не знал `off_target_lang` → дефолт 8, ниже `length` (6). Глава, вернувшаяся не на том языке, показывала «length» как худшую проблему. У функции не было ни одного теста. **Закрыто:** switch стал картой, а исчерпаемость держит тест, читающий константы `FlagReason` **из исходника через go/ast** — список в тесте гнил бы ровно так же, как список в коде, и заверял бы гниль. - -**К. ВТОРОЙ CALL-SITE `classify`** (находка оркестратора). `repairReGate` собирал `classifyInput` руками из тех же полей, что `classifyOutput`. Поле `TargetScripts` однажды уже забыли там, починили руками — и посадка показала, что починку не держит ничто. **Закрыто устранением:** один конструктор `classifyInputFor`, пин на нём через рефлексию — любое поле, пришедшее нулевым, валит тест. Список полей в тесте не пишется: его обновлял бы тот же, кто забыл обновить конструктор. - -**Л. ДВЕРЬ 3 ГЕНДЕРНОГО ТЕСТА ОБЕЩАЛА НЕ ТО, ЧТО ДЕРЖИТ** (находка оркестратора, подтверждена исполнением). Её комментарий говорил: «впустят mined-строки в базовый банк — покраснею». Не краснеет: внутри ОДНОГО прогона черновая волна покупается ДО ответа классификатора, поэтому никакое правило про банк не положит род на тот провод. Утверждение об отсутствии оставлено как утверждение о ПОРЯДКЕ, а денежный контракт теперь утверждается там, где живёт — на двух материализованных банках: обогащённый несёт mined-строку, базовый её не выбирает. - -**М. ИНСТРУМЕНТ ТЕРЯЛ СВОЙСТВО «ГОНЯЕТСЯ ЦЕЛИКОМ»** (находка оркестратора). Протухший якорь `AF-keys-file-reason-covers-all` (протух ещё до этого пака, на `8adcb86`) возвращал ОШИБКУ ПРОГОНА и клал весь каталог на 33-й записи из 120 — а прогоны через `-id` продолжали проходить, и никто не видел. **Закрыто:** протухание стало ПЕРЗАПИСНЫМ вердиктом `ROTTED` — прогон идёт дальше, выход остаётся ненулевым, запись предъявляется. Якорь починен. - -### Что я перепроверила САМА, не приняв на слово -- **Три находки оркестратора, где код на вид противоречил заголовку** (`repair.go` передаёт `TargetScripts`; `CheckPlaceholders` сканирует весь `canon` и ловит незакрытый `{{`; `TestUnionStickyNewestWins` на вид ловит keep-first) — прогнала собственные посадки. Две подтвердились как непокрытость, одна — нет. -- **`UnionSticky` (его находка №4) — ОТВЕДЕНА.** Четыре независимые посадки красные. Оркестратор проверил свою на чистой копии `b8154cd` и снял её сам с точным разбором: его «инверсия» (обход новое→старое с keep-first) ПОВЕДЕНЧЕСКИ ЭКВИВАЛЕНТНА оригиналу (обход старое→новое с перезаписью). **Третий класс ложной зелени за сутки — посадка, не меняющая поведения**, рядом с моими двумя: упавшая сборка, прочитанная как зелень, и обрезанный вывод, прочитанный как ноль. Общее у всех трёх: **читатель верит ВЕРДИКТУ, не проверив, что его произвело.** -- **Дыра `CheckKeys`** — не поверила отчёту агента: написала тест, увидела его красным на всех трёх воротах, потом чинила. -- **Каждая из 19 новых записей каталога предъявлена красной прогоном.** Одна (`WB41`) вернулась SURVIVED — **моя фикстура случайно давала верный ответ и под посадкой** (неподписанная строка была `Confirmed` и сама наполняла набор подписей). Фикстура переписана так, что подпись — единственный возможный источник; предъявлена красной. - -### ⚠ Чего круг НЕ покрыл -- **Причина `S6e`** — совпадение ли это фикстур или у банк-ролей есть второй путь к клиенту помимо `clientFor`. Названо агентом, не открыто; я закрыла СЛЕДСТВИЕ (пин на карте клиентов), не причину. -- **Сквозной прогон дыры `CheckKeys`** через `NewRunner` → волна: закрыто на уровне `CheckKeys` и пре-строенной карты, но что происходит на живом проводе с ключом-пустышкой, никто не гонял. -- **Резюме через смену версии** — end-to-end пере-вердикта резюмированного чанка нет ни одного теста; вся аргументация про `--resnapshot` держится на чтении. -- **Конвенционный путь стадийного промпта** — пер-путевой тест гоняет только `prompt_override`. Маркер внутри вырезаемого комментария проходит по построению, и НАМЕРЕННОСТЬ этого никем не проверена. -- **Два НЕПОДПИСАННЫХ ряда одной поверхности** оба рендерятся: «один приказ на термин» держится только когда подпись есть. Наблюдение агента, теста нет ни в ту, ни в другую сторону. -- **`precomputeSticky`** — сброс на границе главы и запись переноса, заблокированного спойлером. -- **Сам харнесс `tmmutate`** (отпечаток и восстановление) и единственная запись `expect: survives` (`G-byte-gate`) не ревьюились ни одним кругом из трёх. -- **Ракурсы, унаследованные непокрытыми из круга 2 и не открытые снова:** `$0`-проекции на дефектной книге · липкий перенос до редакторского провода сквозным · настоящая латинописьменная цель (`target-en.txt` в репо нет) · `eval/`-харнессы вне модуля. -- **F11 остаётся риском дизайна, не чинен:** порог экрана калиброван на ОДНОЙ сянься-новелле; на латино-тяжёлом здоровом русском даёт ложный `off_target` (0.461 с пиньинь-глоссами, 0.077 с shell-командами), а ложный флаг ЭСКАЛИРУЕТ — то есть стоит платного вызова. Оркестратор забрал носителем. -- **Круг 3 не ревьюил мои правки круга 3.** Копии агента сняты до них. Четвёртый круг, если он будет, начинается отсюда. - -### Числа круга -Батарея `MAKE_EXIT=0`, `0 issues`, все пакеты `ok` (`pipeline` 571 s под `-race`), скипы те же. **Голден в этом круге не трогали, и `TestGoldenDeterminism` проходит против него** — круг не сдвинул ни одного байта провода. Тесты **1156 → 1173**. Каталог **120 → 141**; **все двадцать одна новая запись предъявлена КРАСНОЙ** (`WB41` — со второй попытки, после починки фикстуры, которая случайно давала верный ответ и под посадкой). - -⚠ **КАТАЛОГ ПРОГНАН ЦЕЛИКОМ — впервые, и первый же целый прогон окупил починку инструмента.** 141 запись: **136 RED · 0 SURVIVED · 0 NOTHING · 1 записанный `survives` (`G-byte-gate`, аргументирован) · 4 ROTTED**. Протухли четыре МОИ ЖЕ записи, чьи якоря увезли рефакторы кругов 2–3: `WB5` (сборка `classifyInput` уехала в конструктор), `WB12` (правило дедупа исправлено кругом 2 — запись описывала регрессию, которую я же и починила), `WB16` (у валидатора не стало собственного списка), `WB21` (банк-роли уехали в `gateModels`). **Под прежним поведением прогон лёг бы на первой из них, и трёх остальных никто бы не увидел** — ровно то, что произошло с `AF-keys-file-reason-covers-all` и держалось незамеченным два пака. Все четыре пере-навешены на нынешние выражения тех же свойств (у `WB12` и `WB16` заодно переписан `why`: свойство изменилось, а не только адрес) и предъявлены красными. `AF` в целом прогоне — **RED**, то есть починка якоря проверена не на слово. - -## Файлы - -**Изменены:** `internal/pipeline/{disposition,chunkrun,waverun,events,manifest,mining,terminologist,repair,render,runner}.go` · `internal/membank/{memory,mempostcheck,decisions}.go` · `internal/terminology/{terminology,classify}.go` · `internal/miner/miner_emit.go` · `internal/checks/sanitizer.go` · `internal/config/{book,pipeline}.go` · `internal/lang/embedded.go` · `internal/store/migrate.go` · `cmd/tmctl/render.go` · `cmd/tmmutate/mutations.json` (92 → 96) · `configs/pipeline-{c1,c2,arm-*}.yaml` · `prompts/zh-ru/*.md` (шесть — жанр; `classifier.md` — третье поле) · `example/book.yaml` · тесты и голден. -**Новые:** `internal/langscreen/{langscreen,langscreen_test}.go` · `internal/pipeline/{cuttag_test,gender_wire_test,offtarget_corpus_test,volumepanic_test,offtargetflag_test,contourpreflight_test}.go` · `internal/pipeline/testdata/offtarget-corpus.json` · `internal/config/{bank_contour_test,genre_retired_test}.go` · `internal/checks/latinguard_test.go`. - -## Команды приёмки - -``` -cd backend && make battery # MAKE_EXIT=0, 0 issues, 19 пакетов ok, 3 скипа -go test ./internal/pipeline/ -run 'Gender' -v # производитель рода, три двери доставки -go test ./internal/pipeline/ -run 'OffTargetCorpus|LiveEchoGate|LatinOffTarget|TargetScreen|TwoScreens|ScreenConstants' -v -go test ./internal/langscreen/ -v # изолированный модуль экрана -go test ./internal/config/ -run 'BankContour|BankRoleBudgets|Genre' -v -go test ./internal/membank/ -run 'ApprovingATermKeeps|LawBlockGivesOneOrder' -v # род переживает подпись; один приказ на термин -go test ./internal/pipeline/ -run 'OffTargetCompletionIsFlagged|RunnerHandsTheScreen|ThresholdIsUsed|ContourRefusesAWriteRun|RenderRejectsUnknownPlaceholder' -v -go test ./internal/checks/ -run 'LatinResidueClassIsGated' -v # вторая половина дыры 46, обе стороны -go test ./internal/pipeline/ -run 'ACrashOverACaughtCeiling|StopLineSaysTheGrant|OneCarriedUnit' -v -cp -a backend /tmp/mut/backend && cd /tmp/mut/backend && GOFLAGS=-buildvcs=false \ - go run ./cmd/tmmutate -root . -id WB1-head-always-end-of-book,WB2-head-keyed-on-the-carry,WB3-bound-border-drops-a-single-carry,WB4-cut-tag-drops-the-embedded-data,WB5-target-screen-never-sees-the-target,WB6-off-target-flag-not-escalatable,WB7-bank-role-exemption-narrows-to-one-role,WB8-latin-residue-runs-on-a-latin-target,WB9-contour-configured-and-inert,WB10-missing-contrast-costs-a-wave,WB11-nil-pack-contour-never-runs,WB12-two-orders-for-one-term,WB13-placeholder-refusal-arrives-after-a-paid-wave -grep -rl '{{genre}}' backend/prompts/ | wc -l # 0 -grep -rn '^func Test' backend --include=*_test.go | wc -l # 1143 -python3 -c "import json;print(len(json.load(open('backend/cmd/tmmutate/mutations.json'))))" # 105 -``` - -### 04.09 — записка-план вайр-батча «род · экран цели · заголовки инъекций» (промт `BACKEND_WIREBATCH_SESSION_PROMPT.md`, сессия `textmachine-main-7e`) - -> **Скоуп — шесть пунктов промта.** (1) 210: род производится авто-путём, доказательство сквозным прогоном «произведено → отрендерилось». (2) 113 → 46 в этом порядке: корпус-фикстура и регресс на реальный запас боевого гейта, потом экран цели именованным флагом + вторая половина дыры `detectLatinInsertion`. (3) 134: единый закон-блок редактора, маркер ⟨проверить⟩ с провода, то же у транслятора. (4) 140: банковый контур в `pipeline-c1.yaml`, суб-бюджеты ×4.47. (5) 184(а): `{{genre}}` из шести промптов пары и поле из канона брифа. (6) ДОФИКС чужого пака D39.187 п.4 (две посадки + граница `bound()` + три числа) и ФЧ-7 строки 197 — ПРОВЕРКОЙ, не стройкой. -> **Инварианты.** Не коммичу — дерево на лендинг оркестратору. НЕ двигаю: схему хранилища · `manifestVersion` · `StreamVersion` · версию контракта. Новый эмбед-файл не завожу. `FlagCJKArtifact` не переиспользую (снято D39.93 п.2). Статусы строк и подписная таблица в 134 не трогаются (D39.104 п.1). Семантику переноса слота не трогаю. Текст заголовка редактора не меняю — он байт-идентичен арму `AM-noclause` пробы 18 (`experiments/18-editor-wire-probe.md` §0.3). -> **Развилки, объявленные ДО кода.** (1) род — производится КЛАССИФИКАТОРОМ (третье поле его ответа), а не терминологом и не третьим проходом: ноль новых вызовов, парсер классификатора позиционный и закрыто-словарный. (2) Второй замок (`Confirmed`-фильтр рендера рода) — вынесен пингом оркестратору на ратификацию, сам не снимаю. (3) 134 — снимаю РЕНДЕР в Go, `internal/lang/data/injection.txt` НЕ трогаю: правка данных двигает `EmbeddedVersion` → `cutTag` → `manifest_key` → снос `unit_resolutions` книги ПАРАЛЛЕЛЬНОГО платного прогона платформы. -> **Что НЕ делаю.** Чужие зоны `platform/` `frontend/` `eval/` и соседний worktree. Строки 131 · 105(а) · 14 · 119 · 124 · 115 · 160 · 161 · 142. -> **Самопроверка исполнением.** Дифф `^func Test` пофамильно до/после (удалённых имён 0) · маскированный дифф голдена (только названные версионные оси + ровно две строки диспозиций) · собственные посадки `tmmutate` ВНЕ шести пунктов на копии дерева · отдельный адверсариальный проход свежим контекстом (модель назову) · отчёт о движении `cutTag`: `tmctl manifest` до и после. - -### 02.09 — эхо-записка пака «число согласия на пути второй покупки» (промт `BACKEND_CONSENT_SESSION_PROMPT.md`, сессия `textmachine-77`) - -> **Канала к оркестратору нет:** адрес в `/tmp/textmachine-channel` (`textmachine-33`) в `ListAgents` отсутствует, промт писан на другой машине — вопросы едут секцией отчёта ниже. Дерево на входе чисто, HEAD `494c4ef`; все `file:line`-якоря промта сверены грепом на этой машине и сходятся. -> **Скоуп — четыре пункта в порядке промта:** (1) ФЧ-5, ось МОДЕЛИ: строка прайсится по модели, которую её стадия резолвит СЕЙЧАС (`Stage.ResolvedModel`); округление вверх при незнании = максимум из цен по ответившей модели, по текущей и — для эскалационных вызовов — по текущему хопу; оговорка «оценка, ошибается вверх, вот чем» в ОБОИХ текстах согласия; обещание точности снимается в `status.go` (комментарий у `RebillUSD`) и в шапке `reprice.go`. (2) Строка 232, остаток «слот»: юнит, который прежний прогон НАЧАЛ (есть строки текущих позиций) и не отгрузил по оси `finalStageWave`, слота гранта повторно не тратит — дописывается вне гранта, считается отдельно. (3) ФЧ-4 обе половины: `rebill_historical_rows` в `status --json` под тем же `rebill_basis` + basis в тексте отказа `--accept-rebill=`; одна формулировка basis для CLI-статуса и текста отказа. (4) ФЧ-1: ровно три названные гарантии — сперва воспроизвести мутацию на копии и увидеть зелень, потом посадка тестом + запись в `cmd/tmmutate/mutations.json`. -> **Инварианты:** направление ошибки — никогда не занизить молча (D39.150) · `status` и `translate` — ОДИН вывод · фигура едет с базисом (D39.181 п.1) · ось отгрузки = `finalStageWave`, не draft и не «любая» · схема хранилища, форма манифеста, версия контракта НЕ трогаются · тесты не подгоняются, заказанная смена поведения объявляется (D39.183). -> **Не делать:** строки 160 · 221 · 224 · 226 · 228 · 230 · 246 · 247 · 233 · 229 · 141-остаток · 131 · платный замер · новое поле в кадре `finished` шва (класс «поле в опубликованном документе» — ратификация оркестратора; счётчик «дописан вне гранта» живёт в отчёте прогона и логе, вопрос — в отчёт) · коммиты. -> **Самопроверка исполнением:** дифф `^func Test` (база 1083, снимок имён снят) · собственные посадки `tmmutate` на копии в scratchpad · свежий адверсариальный проход субагентами Fable 5 (число назову) · `make battery` на копии до и на дереве после. - -### 03.09 — ОТЧЁТ пака «число согласия на пути второй покупки» (сессия `textmachine-77`, промт `BACKEND_CONSENT_SESSION_PROMPT.md`) - -> **Итог:** четыре пункта состава исполнены, дерево `backend/` готово к лендингу, **НЕ КОММИЧУ** (`git diff --cached --name-only` → пусто). HEAD на входе `494c4ef`; после согласованного с платформой (`textmachine-37`) `git pull --ff-only` — **`2a63c37`**. Входящее (72 файла) не пересекалось ни с одним моим файлом, кроме `docs/PROGRESS.md`: свою записку сняла редактором, дала git пройти fast-forward, вернула. Индекс не трогала, stash не применяла. -> **Канала к оркестратору нет:** `textmachine-33` в `ListAgents` отсутствует, промт писан на другой машине — вопросы секцией ниже. Все `file:line`-якоря промта сверены грепом здесь и сошлись. -> **Батарея на финальном дереве:** `cd backend && make battery` → `MAKE_EXIT=0`, `golangci-lint` `0 issues`, `ok` в 18 пакетах, SKIP: `TestMinerFullBookParity`, `TestHelperEventsRun`, `TestHelperKillLoop`. ⚠ Скипов ТРИ, а не шесть как в базовом прогоне, и это не ослабление: `TestCheckerLabelsBaseline`/`TestCheckerLabelsCandidates`/`TestK6LabelsBaseline` в реальном дереве корпус находят и ИДУТ — `go test ./internal/checks/ -run TestCheckerLabelsBaseline -count=1 -v` → `--- PASS`, `go test ./... -run TestK6LabelsBaseline -count=1 -v` → `--- PASS`. Базовый прогон гнался на копии в `/tmp`, где маркер корня репозитория не находится. -> **Тесты `^func Test`:** `grep -rn '^func Test' backend --include=*_test.go | wc -l` → **1083 → 1105**; пофамильный дифф двух снимков (`comm -23`) — **удалённых 0**, добавленных 19. - -**Комплектность против заказа — по четырём пунктам и по таблице разметки.** - -**1. ФЧ-5 — ось МОДЕЛИ. Все ЧЕТЫРЕ обязательства D39.150 п.1.** -- **(1) прайс по модели, которую стадия резолвит СЕЙЧАС.** `internal/pipeline/reprice.go`, `currentModelFor(stage, escalation)`: для эскалационного вызова — текущий `ResolvedHop`, иначе `ResolvedModel`. **Именно РЕЗОЛВЕННЫЙ слот, а не конфигурационный `model:`** — на книге с лейблами это разные модели, и прайсить надо по той, которую прогон вызовет (пин `TestTheModelAxisReadsTheRESOLVEDSlotNotTheConfiguredOne`, каталожная запись `FC5-model-axis-configured-slot`). Признак эскалации взят из колонки `checkpoints.escalation` полем `store.CheckpointUsage.Escalation`; **схема НЕ тронута** (`git diff --stat -- backend/internal/store/migrate.go` → пусто), колонка живёт с v3. -- **(2) округление ВВЕРХ при незнании — формула моя.** `now = max( cost(токены @ модель, которая ОТВЕТИЛА), cost(токены @ модель, которую стадия резолвит сейчас) )`. Довод: количество токенов принадлежит ответившей модели, цена — той, которая ответит; ни одна не смеет опустить число ниже другой. Оба направления проверены исполнением: стадия ушла на модель ×10 — число ×10 (замер приёмки `$0.003640` против `$0.036400` воспроизведён тестом); стадия ушла на модель ÷10 — число НЕ падает. База порога `projectBookUSD` считается тем же `usd()`, поэтому обе половины гейта в одной валюте. -- **(3) оговорка в тексте согласия.** `projectionBasis` (`rebill.go`): «…an ESTIMATE that errs upward, never below what the answering model would charge for the same tokens **where their usage is on file**; K of the N unit(s) were **bought from** a model their stage no longer resolves to…». Едет в ЧЕТЫРЁХ операторских местах: пороговый отказ, его scoped-клауза, отказ по названному потолку, и `tmctl status` через `StatusReport.RebillFigureBasis()`. -- **(4) обещание точности СНЯТО в ОБОИХ живых местах, названных промтом.** `git grep -n 'can never quote a different number' -- backend/` → пусто; на входном коммите `git grep -n 'can never quote a different number' 2a63c37 -- backend/ | wc -l` → **1** ⚠ (в первой редакции отчёта стояло «3» — число было снято широким грепом на три разных шаблона и приписано этому, узкому; исправлено дофиксом); `grep -rn 'NOT projected (like the content axis' backend/internal` → пусто. Заменено: шапка `reprice.go` — «ROUTING IS PROJECTED, AND IT ROUNDS UP»; комментарий `RebillUSD` в `status.go` — «what neither of them promises is ACCURACY… an estimate that errs UPWARD **where the tokens are known**». Третья копия обещания (исторический комментарий у `projectStoredMemory`) снята попутно. -- **Решила сама, сверх буквы заказа:** счётчик `RebillProjection.ModelMovedRows` и поле `rebill_model_moved_rows` в `status --json`. Довод: фигура, часть которой — верхняя оценка через смену модели, без этого числа неотличима от точного пере-прайса, а D39.181 §2.3 требует базиса у опубликованной фигуры. Это ВТОРОЕ новое поле сверх названного промтом одного — вопрос 3. - -**2. Строка 232, вторая половина — ЗАКРЫТА ТОЛЬКО ПО ОСТАТКУ «СЛОТ».** -- **Ось отгрузки — `finalStageWave`**, как велено; не draft и не «любая». Проверена исполнением и на редакторском, и на ЧЕРНОВОМ-ТОЛЬКО конвейере (`TestDeliveryIsReadFromTheSHIPPINGWaveOnADraftOnlyPipeline`, `TestACarriedUnitOnADraftOnlyPipeline`). -- **Механизм.** Класс `unitCarried` в `volume.go`: юнит, у которого есть строка ХОТЯ БЫ ОДНОЙ позиции, исполняемой этим конвейером, но не всех, и который не отгружен по оси. Он допускается ВНЕ гранта, но платит; в отчёте — внутри `Delivered` и отдельно в `Carried`. Сравнение с потолком — `granted() = Delivered − Carried + Reworked`, заменило `Paid()` в ОБОИХ местах (`planVolume`, `rescopeEditWave`). -- ⚠ **И сам перенос ОГРАНИЧЕН ГРАНТОМ — не больше `MaxUnits` перенесённых за прогон, в порядке книги.** Без этой границы потолок переставал быть потолком: прогон без `--max-units`, начерновивший всю книгу и умерший до редакторской волны, отдавал следующей покупке ВСЕ свои юниты. Замерено на фикстуре: **грант 1 оплачивал 5 юнитов и 5 вызовов провайдера**, а поскольку в остатке не оставалось ничего, прогон не отчитывался вовсе. Найдено адверсариальным проходом по МОЕЙ ЖЕ работе. Принцип границы, а не число: покупка на N может дописать столько же прерванных юнитов, сколько начинает новых, — работа прогона остаётся пропорциональна гранту. Не влезшее держится как `LeftFresh` («никогда не доставлено»), потому что это ровно оно. -- ⚠ **`bound()` теперь истинен и когда `Carried > 0`,** а не только когда что-то удержано: прогон, дописавший юниты вне гранта, обязан это СКАЗАТЬ, иначе молчание приходится ровно на случай с наибольшим расхождением. -- **Решила сама (промт отдал):** *где вычитается потраченный слот* — нигде: он НЕ БЕРЁТСЯ. *Как переживает рестарт* — носитель «юнит начат» — его же строки `chunk_status` по текущим позициям (`unitStarted`/`unitPositionsOnFile`, одно перечисление с `unitFullyRecorded`, чтобы они не разошлись в понимании «позиции»); схемы не нужно. *Частично двинутый юнит* — carried, дописываются только недостающие позиции. -- **Замер строки исправлен:** было «4 купленных юнита → 2 главы», стало 4 → 4 (`TestAUnitInterruptedBetweenWavesTakesNoSecondSlot`: `Delivered=4 Carried=2 LeftFresh=1`, ровно 6 вызовов во второй покупке). -- **Семантика `--max-units` изменена и объявлена во всех трёх операторских местах** (`cmd/tmctl/invocation.go`, `Runner.MaxUnits`, `backend/README.md`): грант считает юниты, которые прогон БЕРЁТ СЛОТОМ — новые и пере-делываемые; перенесённые названы в строке стопа и в логе (`finished_from_an_earlier_grant`). ⚠ Тексты НЕ утверждают, что «слот за юнит был взят прежним прогоном»: движок этого не знает (прежний прогон мог идти без потолка), и первая редакция текстов это утверждала — снято. -- ⚠ **ОСТАТКИ, которые пак НЕ закрывает — названы в докстринге `deliveredUnits` и здесь:** - 1. **«первая редакторская стадия»** (остаток, который промт велел назвать): конвейер, получающий ПЕРВУЮ редакторскую стадию, двигает отгружающую волну с draft на edit, и уже прочитанные юниты репортятся свежими. Закрытие требует записи о том, ЧЕМ БЫЛА отгружающая волна. - 2. **Прерванная ПЕРЕ-ДЕЛКА — новый остаток, найденный этим паком.** Отгруженный юнит, чья пере-делка оборвана между волнами, имеет строку в КАЖДОЙ позиции, снова классифицируется `unitRework` и снова берёт слот. Отличить его от обычного банк-онли сдвига (черновик free, редактор пере-оплачивается — определение rework) можно только записью «какой прогон купил какую строку». **Строку 232 закрываю только по остатку «слот», как и велено.** - -**3. ФЧ-4 — ОБЕ половины, фигура едет с базисом (D39.181 п.1).** -- **(а)** `HistoricalRows` больше не теряется на границе `status.go`: `rebillOutcome` возвращает проекцию ЦЕЛИКОМ (под `none`/`failed` — нулевую структуру, так что новое поле не может пережить «неизвестный» ответ, забытое в одной ветке), в `StatusReport` — `rebill_historical_rows` и `rebill_model_moved_rows` под тем же `rebill_basis`. -- **(б)** отказ по названному потолку `--accept-rebill=` несёт `projectionBasis(proj)` — базис ТОЙ ЖЕ run-scoped проекции, чьё число печатает. -- **Сверх заказа, по тому же правилу «каждая цифра предложения — из его проекции»:** scoped-клауза ПОРОГОВОГО отказа печатала число этого прогона рядом с книжным базисом; теперь несёт свой (`TestTheScopedClauseOfTheRefusalCarriesItsOwnBasis`). Дефект был и до пака. -- **Один вывод на два канала:** `StatusReport.RebillFigureBasis()` → строка RE-PAYMENT в `tmctl status`; `TestABilledDecodeRowIsDisclosedAsOldMoneyEndToEnd` сверяет, что проекция, `--json`, CLI и ОБА текста отказа называют одну строку раскрытия. -- **Решила сама (промт отдал: имя поля · форма basis · что печатать при `HistoricalRows=0`).** *Имя* — `rebill_historical_rows`, в форме соседей по префиксу `rebill_*`. *Форма basis* — НЕ второе поле-строка рядом с числом, а одна формулировка `projectionBasis`, которую метод `RebillFigureBasis()` переиспользует для CLI: существующий `rebill_basis` остаётся КАЧЕСТВЕННОЙ осью (`pending`/`stored`/`none`/`failed`), а новые счётчики — количественной, и словами их описывает один текст, чтобы гейт и статус не разошлись формулировкой. *При нуле* — печатается ноль, а не пропуск поля: `omitempty` здесь тот же дефект, ради которого его сняли с `rebill_units`/`rebill_usd` — отсутствие поля неотличимо от «свободно». Ноль под `pending` значит «всё пере-оценено», под `failed` — «не считалось»; различает их `rebill_basis`. В операторском тексте при нуле клауза просто не печатается: предложение не должно нести оговорку, у которой нет предмета. - -**4. ФЧ-1 — ровно три названные гарантии, по каждой все ТРИ шага.** -- **Шаг 1 — воспроизвести на копии ДО пака.** `cp -a backend /tmp/.../mut0/backend`, `go run ./cmd/tmmutate -root . -catalog prepack_mutations.json` → **все пять посадок SURVIVED**: фильтр по книге в ОБОИХ пакетах, ключ ячейки без `chunk_idx` в ОБЕИХ формах, снятие пометки «не пере-оценена». ⇒ гарантии (2) и (3), принятые приёмкой СО СЛОВ линзы, подтверждены исполнением; ни одна не оказалась уже прибитой. -- **Шаг 2 — посадки тестом.** (1) `TestCheckpointUsageForBookIsScopedToTheBook` (две книги в одной БД, ОДНА позиция) + `TestAnotherBooksMoneyNeverEntersTheConsentNumber`. (2) `TestEveryChunkOfAChapterIsRePricedFromItsOwnCalls` — глава из нескольких чанков РАЗНОЙ стоимости: единственная форма, в которой пул ячейки виден (во всех прежних фикстурах глава = один чанк, потому мутация и переживала). (3) `TestABilledDecodeRowIsDisclosedAsOldMoneyEndToEnd` — продовый путь до `--json`, CLI и обоих отказов. -- **Шаг 3 — каталог.** `cmd/tmmutate/mutations.json`: **73 → 92**. Четыре записи `FC1-*` на три гарантии (фильтр по книге пинится в двух пакетах) плюс две `FC5-*` на несущие свойства, которые пак СОЗДАЛ (граница переноса и резолвенный слот) — их я завела по той же логике, по которой каталог существует. Денежный путь в каталоге: греп промта считает СТРОКИ — `git show 2a63c37:backend/cmd/tmmutate/mutations.json | grep -c 'reprice.go\|rebill.go\|volume.go\|ledger.go'` → **0**, тот же греп по дереву → **17**; различных ФАЙЛОВ денежного пути — **0 → 4** (`reprice.go`, `rebill.go`, `volume.go`, `store/ledger.go`). ⚠ В первой редакции стояло «стало 5 файлов» — такого числа не даёт ни одно прочтение; исправлено дофиксом. Прогон всех шести на свежей копии: **6 RED, «0 unexpected outcome(s)», exit 0**. -- Четвёртого одноразового харнесса не писала; `tmmutate` гоняла только на копиях в scratchpad. -- **Решила сама (промт отдал: форма посадок · уровень теста · текст `why`).** *Форма* — ломать ровно то утверждение, которое гарантия держит, минимальной правкой, и обязательно КОМПИЛИРУЮЩЕЙСЯ: рецепт, оставляющий переменную неиспользованной, даёт `tmmutate` вердикт «ничего не выполнялось», а не «красное», — на этом я поймала два своих же докстринга и переписала их. *Уровень* — по тому, где живёт защита, а не где живёт правка: фильтр по книге пинится и в `store`, и в `pipeline` (мутация пережила обе батареи по отдельности, значит одной мало); ключ ячейки — только `pipeline`, дефект в `newRepricer`; пометка «строка не пере-оценена» — `pipeline`, и именно ПРОДОВЫМ путём, потому что промт называл её не ловящейся на нём. *Текст `why`* — называет СВОЙСТВО и причину, по которой его не видно («во всех прежних фикстурах глава = один чанк»), а не описание правки: `why` читают в тот момент, когда мутация выжила и надо понять, что именно перестало охраняться. - -**Правки СУЩЕСТВУЮЩИХ тестов — объявляются (D39.183).** -- `TestAFlaggedMemberWithNoEditRowIsNotFree` (volume_test.go): описывал «юнит без edit-строки ОБЯЗАН потратить грант» — поведение, которое заказывает пункт 2. Гарантия «не FREE, платит и отчитывается как платящий» СОХРАНЕНА; гарантия «берёт слот» уехала в противоположную. Фикстура расширена до 4 глав; после введения границы переноса ожидание — `Delivered=2 Carried=1 LeftFresh=2`. -- `TestAnAnnouncedButFLAGGEDUnitIsStillNewBook`, `TestAUnitWhoseEDITFlaggedIsStillNewBook` (volumedelivery_test.go): флагнутый юнит после добавления стадии теперь carried. Гарантии «анонс ≠ текст» и «судить по ОТГРУЖАЮЩЕЙ строке» сохранены; фикстуры расширены до 3 глав, сигнатуры названных мутаций пере-выведены. ⚠ Рецепты мутаций в их докстрингах были НЕИСПОЛНИМЫ (правка на месте вызова оставляла `shipStages` неиспользованным, пакет переставал собираться, и `tmmutate` дал бы NOTHING вместо RED) — переписаны на правку тела `unitShipped`. -- Смена сигнатур (`repriceCheckpoint`/`usd` — третий возврат `moved`; `rebillOutcome` — проекция целиком): `TestRepriceCheckpointFallsBackToTheBilledAmount` (таблица расширена до 11 случаев), `TestRepricerCountsOnlyTheCurrentGenerationOfCheckpoints` (+4 строки), `TestAFailedBasisNeverCarriesAFigure` (сравнивает всю структуру с нулевой — стал строже), `TestRebillConsentCapBelowProjectionRefuses` (+утверждение про базис), **`TestCheckpointsOfOneGenerationSumToTheRowsCost`** (адаптирован под третий возврат `usd()`; ослабления нет, правка механическая — ⚠ в первой редакции отчёта имя пропущено, дописано дофиксом), `reprice_model_test.go` и `render_test.go` (формулировка оговорки: «were answered by» → «were bought from», см. ниже). -- `TestStatusPricesTheDriftWithTheGatesOwnNumber` — тронут ТОЛЬКО комментарий, цитировавший снятое обещание. Тест не менялся. -- **Удалённых имён — 0.** - -**Мандат самопроверки — три артефакта.** -1. **Дифф `^func Test` исполнением:** 1083 → 1102, удалённых 0, добавленных 19. -2. **Собственные адверсариальные посадки ВНЕ трёх гарантий:** каталоги `own_mutations.json` (26), `round2_mut.json` (5), `round3_mut.json` (2), `draftonly_mut.json` (2) — **35 посадок**, `tmmutate` на копиях. Итог: **все RED**. ⚠ В первом круге ДВЕ пережили, и это находки: **X21** (`rescopeEditWave` сравнивал грант через `Paid()`, то есть считал перенесённый юнит за слот и отказал бы в свободном) и **X22** (строка стадии, которой конвейер больше не исполняет, засчитывалась как «юнит начат»). Обе прибиты (`TestTheRePlanCountsSlotsNotCarriedUnits`, `TestRowsOfARetiredStageAreNotAStart`) и во втором круге RED. -3. **Проход по СВОЕЙ готовой работе свежим контекстом.** Воркфлоу из семи линз (направление ошибки · раскрытие на новой границе · слот и черновой конвейер · вывод членства `usd()` · правда текста · качество тестов · скоуп и заказ), каждая находка судится тремя независимыми опровергателями (корректность · воспроизведение · сверка с промтом), большинство решает. Модели: линза качества тестов — Fable 5, остальные — Opus 5 (ограничение владельца на число Fable-агентов соблюдено: один). **Числа прохода: 7 линз, 27 находок (2 HIGH · 12 MED · 13 LOW), 42 вердикта верификаторов, 46 агентов.** ⚠ Верификаторов «подтверждено» — НОЛЬ, и читать это надо точно: фаза верификации шла по дереву, где все 27 находок были уже исправлены, то есть опровергатели подтверждали ПРАВКИ, а не отсутствие дефектов. Каждая находка ниже воспроизведена или прочитана мной по коду до правки. - **Находки и диспозиции (все ИСПРАВЛЕНЫ, каждая с посадкой):** - - **[HIGH] перенос без границы обнулял потолок** — грант 1 оплачивал 5 юнитов, и прогон об этом молчал. Введена граница `Carried ≤ MaxUnits` + `bound()` по `Carried`; посадки `FC5-carry-unbounded` и `FC7-volume-stop-silent-outside-grant` (в первой редакции вторая называлась разведочным именем `Y2-silent-when-nothing-left`, которого в каталоге нет — переименовано дофиксом, Б7). - - **[MED] `reconcile` мог вывести `Delivered` ниже `Carried`** — `granted()` уходил в минус, строка стопа говорила «0 NEW unit(s) delivered (1 of the new ones had been started…)», предложение, которое не может быть истинным. Все ветви теперь держат вложенность; посадки `FC7-reconcile-free-branch` и `FC6-reconcile-fresh-branch` (в первой редакции стояло разведочное `Y3`, которого в каталоге нет — переименовано дофиксом, Б7). - - **[MED] операторские тексты утверждали «слот за юнит был взят прежним прогоном»** — движок этого не знает. Снято во всех трёх местах. - - **[MED] ось модели не была прибита на РЕЗОЛВЕННОМ слоте** — чтение `st.Model` вместо `st.ResolvedModel` переживало весь пакет. Пин + каталожная запись `FC5-model-axis-configured-slot`. - - **[MED] $0-строка без usage под сдвинутой стадией** публиковалась как $0 без оговорки. Теперь помечается «не удалось пере-оценить»; посадка `FC7-zero-usage-row-silent-after-a-move` (в первой редакции — разведочное `Z2`). - - **[MED] scoped-клауза порогового отказа шла без своего базиса** (дефект был и до пака) — исправлено, посадка `FC7-scoped-clause-without-basis` (в первой редакции — разведочное `X29`). - - **[LOW] ветка отката (`overshoot`) причисляла строку к «пере-прайсенным по сдвинутой модели»**, хотя цитирует БИЛЛЕННУЮ сумму, не прайсенную ни по одной сегодняшней таблице. `moved` там снят; посадка `FC7-overshoot-claims-a-model-move` (в первой редакции — разведочное `Y4`). - - **[LOW] `moved` считается против `ModelRequested`, а пять текстов говорили «модель, которая ОТВЕТИЛА»** — на провайдере, канонизирующем слаг (`gpt-5-mini` → дата), это разные вещи. Код верен (пере-прогон запросит модель стадии), тексты приведены к нему. - - **[LOW] комментарий про `zeroRow` утверждал, что хвост $0-вызовов — ОДНО поколение.** Это неверно: $0-стадия оставляет `cost_usd == 0` после каждого прогона, поколения неразличимы, и число может быть кратным. Направление при этом ратифицированное (вверх), и оговорка «errs upward» его покрывает; ложное утверждение из комментария снято, ограничение названо вслух. - - **[LOW] комментарий про redrive** утверждал, что пере-атакованный юнит «начинается заново». Неверно: `ResetChunkStages` удаляет только FLAGGED и SKIPPED стадии, черновая `ok`-строка остаётся, и юнит остаётся carried. Комментарий приведён к коду. - - **[LOW] доки кадра `finished`** (`runevents.VolumeLedger`) отправляли читателя к полю `Free` за объяснением «почему юнитов больше, чем грант», хотя теперь причина может быть и в `Delivered`. Тексты исправлены; **поля на провод НЕ добавлено** — вопрос 1. В `runevents/` изменены ТОЛЬКО комментарии (проверено: дифф без строк кода), `StreamVersion` не тронут. - - **[LOW] докстринг `CheckpointUsageForBook` ссылался на несуществующий id каталога** `FC1-book-filter` (в каталоге две записи, `-store` и `-pipeline`); `tmmutate -id` матчит точно, и следующая сессия получила бы «ничего не выбрано». Исправлено. - - **[LOW] комментарий у `RebillHistoricalRows` называл платформу потребителем поля** — её аллоулист не берёт ни одного `rebill_*`. Формулировка приведена к факту: потребитель — операторская оснастка, читающая документ; платформа поле игнорирует. - - **[LOW] несколько комментариев рассказывали ИСТОРИЮ починки** («used to», «went stale once already», «is how four bought units became two chapters») вместо описания кода — закон владельца. Переписаны; провенанс оставлен только в докстрингах ТЕСТОВ, где он в зоне принят. - - **[LOW] «первая половина» текста согласия обещала «never below…» без оговорки** про строки, которые пере-оценить не удалось: они как раз могут стоять НИЖЕ сегодняшней таблицы. Добавлено «where their usage is on file» и «may sit below today's table». - -**Три оси ревью, названные промтом уязвимыми именно в этом паке — по каждой предъявляю.** -1. **Направление ошибки.** Двойной счёт искала специально: порог сравнивает базу и сумму, посчитанные ОДНИМ `usd()` (тест сверяет `projected_book_usd`, а не только сумму), максимум из двух цен берётся РОВНО один раз на вызов, добавка денег банк-ролей входит в опубликованную проекцию и НЕ входит в базу порога — как ратифицировано. Перелёт порога проверен с обеих сторон: число выросло там, где занижало, и не упало там, где стадия ушла на дешёвую модель. Одну форму двойного счёта ревью всё же нашло — кратность оценки $0-строки; направление у неё безопасное (завышение), ограничение названо вслух и в коде, и в секции «не проверено». -2. **`HistoricalRows` на новой границе.** Не теряется и не удваивается: единственный инкремент на строку, `rebillOutcome` отдаёт проекцию целиком, под «неизвестно» — нулевую структуру. Текст отказа и `--json` называют ОДНО число с ОДНИМ базисом — это утверждает тест, сравнивающий подстроку `RebillFigureBasis()` внутри текста отказа, а не два независимых ожидания. -3. **Слот гранта на черновом-только конвейере.** Проверено исполнением, а не рассуждением: `TestACarriedUnitOnADraftOnlyPipeline` — ось не выродилась в «никогда ничего не отгружено» (юнит, начатый и не отгруженный, опознаётся и дописывается), и не выродилась в обратную сторону (уже отгруженные не пере-продаются — `TestDeliveryIsReadFromTheSHIPPINGWaveOnADraftOnlyPipeline`). - -**Что НЕ удалось и что НЕ проверено.** -- **Остаток «первая редакторская стадия»** — вне скоупа, требует носителя прошлой отгружающей волны (схема). -- **Остаток «прерванная пере-делка»** — найден этим паком, требует записи «какой прогон купил какую строку» (схема). -- **Кратность оценки $0-строки:** книга, у которой $0-стадия пере-куплена N раз, а потом уведена на платную модель, получает оценку, кратную N. Направление безопасное (завышение) и объявлено в тексте согласия, но точным его не сделать без маркера поколения на `checkpoints` — та же схемная развилка, что в докстринге `usd()`. -- **Юнит, начатый НЕОГРАНИЧЕННЫМ прогоном,** слота в следующем ограниченном не берёт (но перенос ограничен грантом). Замерено тестом, на живой книге не проверялось. -- **Взаимодействие переноса с ДЕНЕЖНЫМ потолком** на длинном хвосте начатых юнитов не замерено: по построению объёмный потолок их не считает, держит денежный. -- **Живой прогон с платными вызовами не делался** — промт его не заказывал. Вендор-сверок нет: пак не трогает слаги моделей. - -**⚠ Якоря в ЧУЖИХ доках, уехавшие ИЗ-ЗА пака — править не могу, зона не моя; вот готовый список с новыми номерами** (`python3 docs/scripts/counts.py --lint`; все шесть были ЦЕЛЫ на HEAD `2a63c37`, проверено `git show HEAD:<файл> | sed -n 'p'`): - -| док:строка | якорь был | цель сейчас | -|---|---|---| -| `docs/architecture/15-money-path.md:18` | `store/ledger.go:381` «SpentUSD reports (committed, reserved)» | `:392` | -| `docs/architecture/15-money-path.md:24` | `pipeline/status.go:290` «book_ceiling_usd,omitempty» | `:311` | -| `docs/architecture/15-money-path.md:24` | `pipeline/status.go:788` «100 * (committed + reserved)» | `:811` | -| `docs/PROGRESS.md:179` (тело строки 232) | `pipeline/status.go:866` «IT IS NO LONGER THE READ PATH'S FIRST ANSWER» | `:901` | -| `docs/archive/prompts/BACKEND_CONSENT_SESSION_PROMPT_2026-09-02.md` (строка 52 на момент выдачи; в архиве номера сдвинул баннер исхода) | `pipeline/status.go:217` «can never quote a different number» | **снят по заказу (ФЧ-5 п.4)** | -| `docs/archive/prompts/BACKEND_CONSENT_SESSION_PROMPT_2026-09-02.md` (строка 54 на момент выдачи) | `pipeline/reprice.go:24` «NOT projected (like the content axis» | **снят по заказу (ФЧ-5 п.4)** | - -Первые четыре — сдвиг номеров от моих вставок; последние два промт заякорил на тексте, который сам же велел снять, поэтому их «поломка» и есть признак исполнения. - -⚠ **Про остальные 12 я сперва написала здесь «все в `docs/experiments/**`, пришли с полигонным мержем» — это было неверно, и поправлено разбором с платформенной сессией.** Перечисляю по владению; ⚠ **общее число гейта — величина ПОДВИЖНАЯ**, обе смены правят дерево параллельно: на момент того разбора `python3 docs/scripts/counts.py --lint` печатал 18, на момент сдачи дофикса — **20** (в отчёте пака стояло «все 18 строк», и это было верно только для того часа). Разбивка по владению взята с той, 18-строчной выдачи: -- **4 — платформенной сессии** (`textmachine-37`): `docs/PROGRESS.md:19`, `docs/archive/prompts/PLATFORM_P13_SESSION_PROMPT_2026-09-02.md` (строка 286 на момент выдачи) и `docs/archive/prompts/BACKEND_CONSENT_SESSION_PROMPT_2026-09-02.md` (строка 227 на момент выдачи) целятся в одну снятую ею строку рантбука `platform/deploy/README.md:213` «сначала платформа, потом движок»; плюс `docs/archive/prompts/PLATFORM_P13_SESSION_PROMPT_2026-09-02.md` (строка 166 на момент выдачи) на уехавший `Quarantine`. ⚠ Третий из них живёт В МОЁМ промте (это его ⛔-оговорка о том, что совет рантбука опасен), но сломан не мной — целью владеет платформа. -- **8 — полигонных**, пришли с мержем `2a63c37`: семь в `docs/experiments/**` и один в теле бэклога — `docs/PROGRESS.md:190` на `eval/conformance.py:48`. -- ⚠ Якорь в `docs/experiments/23-editor-tier.md:6196` целится в строку 679 этого журнала, которой в нём нет: он был мёртв и до пака (тогда в файле было 472 строки), а мои вставки лишь меняют общее число, которым гейт объясняет смерть. ⚠ Записан здесь СЛОВАМИ, без токена «путь:номер» в обратных кавычках: гейт читает такой токен как якорь, и первая редакция этой строки сама добавила себе двадцатую поломку. - -**Урок, который стоит записи:** первую версию этого абзаца я построила на подсчёте, не перечислив предмет, — тот же класс, что и мои прошлые ошибки «вывести отсутствие из счёта». Счёт по подстроке пути (`grep -c 'platform/'`) тоже не есть счёт по владению: у платформы один из четырёх якорей целится ОТНОСИТЕЛЬНЫМ путём и в такой счёт не попадал. - -**Вопросы, вынесенные владельцу, и его слово (03.09) — записываю, чтобы решения не пере-открывались.** -1. **Поле `carried` в кадре `finished` шва** (`runevents.VolumeLedger`). Спрошено: добавлять ли число «сколько глав дописано сверх гранта», ведь `delivered` теперь может превышать `max_units`. Цена вопроса названа: это класс «новое поле в опубликованном документе» — ратификация оркестратора плюс минорный бамп `StreamVersion`, а промт запретил двигать контракт; платформа кадр `Volume` сегодня не декодирует вовсе (`platform/internal/ingest/events.go`: `Finished{Outcome}`), потребителя у факта нет. **РЕШЕНИЕ ВЛАДЕЛЬЦА: оставить как есть, поле не добавлять.** Исполнено: поля нет, объяснение живёт словами в докстрингах `Delivered` и `Free` того же кадра. -2. **Семантика «юнит, начатый прогоном БЕЗ объёмного потолка, слота в следующей покупке не берёт; перенос ограничен размером гранта».** Альтернативы названы: тратить на такие юниты купленное (тогда покупатель платит за главу дважды — ровно вред строки 232) либо завести схемный носитель «какая покупка оплатила какую строку». **СЛОВО ВЛАДЕЛЬЦА: вынести оркестратору, решение отложено — «подумаем на этот счёт».** ⇒ пункт остаётся ОТКРЫТЫМ вопросом к приёмке; в коде сейчас первый вариант, ограниченный грантом. -3. **`rebill_model_moved_rows`** — второе новое поле `status --json` сверх названного промтом одного; говорит, какая часть суммы завышена из-за смены модели стадии. Аддитивно, платформой игнорируется (её аллоулист не берёт ни одного `rebill_*`); без него человекочитаемая строка и текст отказа перестают говорить дословно одно и то же. **РЕШЕНИЕ ВЛАДЕЛЬЦА: оставить.** -4. **Ошибок в промте не нашла.** - -### 03.09 — ДОФИКС по приёмке №22 (наряд `docs/archive/prompts/BACKEND_DOFIX_2026-09-03.md`, сессия `textmachine-77`) - -> **Состояние git на входе в дофикс — важнее прочего, потому что наряд писан по другому.** Наряд говорит «в индексе лежит незакоммиченная работа двух смен». К моменту исполнения владелец уже закоммитил её в ЛОКАЛЬНЫЙ `e85295d` «temp», а `origin/main` был обновлён ПРИНУДИТЕЛЬНО на `ea54355`, в историю которого `temp` не входит (`git merge-base --is-ancestor e85295d origin/main` → нет). По слову владельца я откатил temp и подтянул наряды двумя командами: `git reset --mixed 2a63c37`, затем `git merge --ff-only origin/main` (перемотка — `ea54355` растёт из того же `2a63c37`). Перед этим поставил метку **`temp-backup-e85295d`**, чтобы коммит с работой обеих смен нельзя было потерять; снимать её до лендинга не надо. Целостность проверена, а не предположена: изменённые файлы совпали с temp **бит-в-бит** (сравнение хешей двух диффов), пять добавленных файлов снова стали неотслеживаемыми и лежат на диске побайтно как в коммите (`git hash-object` против `git rev-parse e85295d:<файл>`). В дереве НА МОМЕНТ ОТКАТА было 44 файла: 22 мои, 21 платформы, один общий журнал; дальше число растёт у обеих смен по мере работы. **Индекс пуст, не коммичу.** -> -> **Батарея после дофикса:** `cd backend && make battery` → `MAKE_EXIT=0`, `golangci-lint` ``0 issues``, `ok` в 18 пакетах, SKIP: `TestMinerFullBookParity`, `TestHelperEventsRun`, `TestHelperKillLoop`. -> **Тесты `^func Test`:** `grep -rn '^func Test' backend --include=*_test.go | wc -l` → **1083 → 1105** (пак довёл до 1102, дофикс и правки по проверяющему добавили остальное), удалённых **0** (`comm -23` по снимкам имён). -> **Каталог `cmd/tmmutate/mutations.json`:** **73 → 92** (`python3 -c "import json;print(len(json.load(open('backend/cmd/tmmutate/mutations.json'))))"`), прирост **19** записей: паком **6** (`FC1`×4 — три названные гарантии, `FC5`×2 — свойства, которые пак создал), дофиксом и разбором его проверки **13** (`FC6`×4, `FC7`×5, `FC8`×3). ⚠ В первой редакции этой строки стояло «18 заведено дофиксом и проверкой», а перечень в скобке давал 14 и не называл `FC1` — число одной величины под именем другой, тот же класс, что чинит Б8. Команда: `python3 -c "import json;from collections import Counter;d=json.load(open('backend/cmd/tmmutate/mutations.json'));print(Counter(m['id'].split('-')[0] for m in d if m['id'][:3] in ('FC1','FC5','FC6','FC7','FC8')))"`. - -**По каждому пункту Б1–Б9.** - -**Б1 — стоп-строка и лог лгали в случае, который пак создал. ЗАКРЫТО.** -Воспроизвёл сам, до правки: `VolumeStop{MaxUnits:2, Delivered:2, Carried:2}` → `Left()=0 granted()=0 Paid()=2`, и предложение печатало разом «not on money and **not at the end of the book**» и «Still in the book: **0** unit(s) NEVER delivered». -Правка: у `String()` теперь ДВА открытия по `Left()`. Грант придержал работу → прежний текст. Книга кончилась → «reached the END of the book under a VOLUME ceiling (--max-units N), which held nothing back», и хвост «Nothing is left in the book» вместо перечисления нулей. Смешанный случай отдельного текста не требует: это первая ветка с уже существующей клаузой про перенесённые. Тот же развилкой пошёл лог `waverun.go` (сообщение выбирается тем же предикатом), а протухший комментарий у `scope.bound()` пере-написан на то, что код делает: «set when the grant held work back OR when the run did work outside it». -Пин: `TestARunThatWorkedOutsideItsGrantSaysSo` дополнен — требует и «reached the END of the book», и «Nothing is left in the book», и ЗАПРЕЩАЕТ «not at the end of the book». - -**Б2 — ратифицированный контракт кадра `finished.volume` стал ложным. ЗАКРЫТО, поля НЕ добавлял.** -Утверждение «present only when a volume grant actually held work back» снято во всех ЧЕТЫРЁХ местах: `runevents.go` (объявление поля и абзац «its PRESENCE is the boolean»), `events.go` (`volumeLedger`), `bookrun.go` (`BookResult.Volume`). Проверка: `grep -rn 'only when the grant actually held something back\|present only when a volume grant actually held\|nil when no grant held anything back\|rather than because the book ended' backend/internal` → пусто. -Новая формулировка говорит, что присутствие значит «грант был в силе и прогону есть что о нём сказать», и что различают два случая `left_fresh`/`left_rework` — оба ноля значат «книга кончилась». Там же прямо сказано, что счётчика перенесённых на проводе НЕТ и почему: владелец 03.09 сказал «оставить как есть». Ратификационная ссылка сохранена с оговоркой, что правило присутствия расширилось вместе со строкой 232. - -**Б3 — три свойства не были прибиты. ЗАКРЫТО, каждое воспроизведено и посажено.** -Сначала воспроизвёл все три на копии — **все три SURVIVED**, как и писала приёмка. После правки те же три посадки на свежей копии → **все три RED**. -- **(а)** ветка `unitFresh` в `reconcile`: новый `TestReconcileKeepsContainmentOnTheFreshBranch`. ⚠ Состояние в нём СКОНСТРУИРОВАНО, а не наблюдено, и это сказано в докстринге: `Delivered == Carried` с исходом класса `unitFresh` не даёт сегодня ни одна фикстура. Утверждаю инвариант всё равно, потому что код заявляет его для КАЖДОЙ ветки. -- **(б)** отказ по названному потолку: `TestTheScopedClauseOfTheRefusalCarriesItsOwnBasis` дополнен половиной про `--accept-rebill=` — требует «1 of the 1» и запрещает «2 of the 2». -- **(в)** оговорка про смену модели при нуле: `TestProjectionBasisNamesWhatTheAmountIsMadeOf` получил отрицательный пин, симметричный тому, что был у близнеца про старые деньги. -Записи каталога: `FC6-reconcile-fresh-branch`, `FC6-capped-refusal-borrows-book-basis`, `FC6-model-caveat-printed-at-zero`. - -**Б4 — докстринг `classifyUnits` не пере-считан. ЗАКРЫТО.** «three things this run can do to it» → «four»; проверено, что членов `unitClass` действительно четыре (`sed -n '/^const (/,/^)/p' … | grep -c '^\tunit'` → 4). - -**Б5 — пример в `README.md:73` противоречил новой семантике. ЗАКРЫТО.** «оплатить не больше 10 выходных юнитов» → «не больше 10 выходных юнитов возьмут слот», в согласии с телом README:11. - -**Б6 — шов `store → репрайсер` прибит с одной стороны. ВЗЯЛ, хотя пункт «по желанию».** -Довод: мой же аргумент по фильтру книги («мутация пережила обе батареи по отдельности, значит одной мало») применим и здесь, и отказ от него был бы непоследовательностью, а не решением. Новый `TestCheckpointUsageCarriesTheEscalationFlag` в `internal/store` + запись `FC6-escalation-flag-store`; подмена `c.escalation` на `0` теперь краснеет и в store-пакете. - -**Б7 — отчёт ссылался на ПЯТЬ несуществующих посадок как на живые гейты. ЗАКРЫТО заведением, а не снятием ссылок.** -Проверил каждую из пяти на копии: **все пять ловятся существующими тестами** — то есть свойства охраняются, но каталог их не помнил, а отчёт называл гейтами. Развязка честная только одна: сделать их настоящими записями. Заведены `FC7-volume-stop-silent-outside-grant`, `FC7-reconcile-free-branch`, `FC7-zero-usage-row-silent-after-a-move`, `FC7-scoped-clause-without-basis`, `FC7-overshoot-claims-a-model-move`; все пять пере-посажены → RED. - -**Б8 — четыре числа не печатались командами, которые отчёт для них называл. ЗАКРЫТО, каждое пере-считано.** -- «денежного пути стало 5 файлов» — такого числа не даёт ни одно прочтение. Правда: греп промта считает СТРОКИ, `0 → 17`; различных ФАЙЛОВ денежного пути `0 → 4`. Обе команды вписаны рядом с числами. -- «на входе 3 попадания» у грепа про снятое обещание — правда **1**: `git grep -n 'can never quote a different number' 2a63c37 -- backend/ | wc -l` → 1. Тройка была снята широким грепом на ТРИ разных шаблона и приписана этому, узкому. Тот же класс, что и мои прошлые ошибки со счётом: число из одной команды под именем другой. -- «все 18 строк» линта — на момент сдачи дофикса гейт печатает **20** (`python3 docs/scripts/counts.py --lint`). Число ПОДВИЖНОЕ: параллельная платформенная смена правит свои файлы, и её якоря уходят и возвращаются. В отчёт пака вписана оговорка об этом. -- «в файле 485 строк» — сейчас **572** (`wc -l < docs/PROGRESS.md`); число снималось до того, как отчёт дорос, и с каждой вставкой меняется. Формулировка в отчёте пака переписана так, чтобы не зависеть от него. -- ⚠ **Отчёт пака сам добавлял себе поломку гейта:** строка таблицы называла чужой мёртвый якорь ТОКЕНОМ «путь-двоеточие-номер» в обратных кавычках, и гейт читал её как якорь ЭТОГО журнала. Переписана словами. ⚠ И первая редакция этого самого пункта наступила на то же место второй раз — объясняя поломку, она снова процитировала токен и снова его создала. Теперь ни одна строка обеих моих записей токена не содержит; проверка — грепом по выдаче гейта на строки этого журнала в моих секциях, пусто. - -**Б9 — неполнота самообъявления по D39.183. ЗАКРЫТО.** `TestCheckpointsOfOneGenerationSumToTheRowsCost` дописан в блок самообъявления: адаптирован под третий возврат `usd()`, ослабления нет, правка механическая. - -**Б10 — к сведению, не заказ; называю, чтобы не умерло молча.** -- **Подсказка в докстринге `Delivered` ключуется на `delivered > max_units`,** а в форме «весь остаток перенесён» `delivered == max_units` — подсказка не срабатывает, и разложить кадр читателю нечем. Связано с отложенным вопросом владельца о семантике переноса; чинится вместе с ним. -- **В перенос попадает и юнит, который ФЛАГНУЛСЯ и текста не отдал:** он «начат и не отгружен», значит следующая покупка снова платит за его пере-атаку вне гранта. Цикл из трёх последовательных покупок не гонял никто — ни приёмка, ни я. Вход в решение владельца, не дефект. - -**Проверка дофикса свежим контекстом — один агент Fable 5, ограничение владельца соблюдено.** Он прочитал исходный промт, наряд, оба моих отчёта и весь дифф, посадил 26 своих мутаций и пере-гнал батарею на копиях. Из его семи находок **три взяты в работу и исправлены**, четыре названы ниже как известные пробелы: -- **[HIGH] `$0`-вызов ВНУТРИ платного поколения выпадал из обхода** — и уносил с собой сегодняшнюю цену. Форма: черновая стадия на локальной `$0`-модели с ПЛАТНЫМ `escalate_to`; стоимость строки объясняет один хоп, поэтому обход останавливался и бесплатный primary терялся, а после сдвига стадии на платную модель число согласия выходило вдвое ниже реальной пере-покупки, БЕЗ оговорки. Воспроизвёл сам на юнит-уровне до правки: `usd=0.010000 fromHistory=false moved=false` при пере-покупке `0.03`. Правило переписано: **вызов, стоивший ноль, никогда не останавливает обход и никогда не переполняет счёт** — он берётся всегда, а обход стоит на первом ПЛАТНОМ вызове после того, как деньги строки объяснены. Цена решения названа в коде вслух: бесплатный вызов на хвосте СТАРОГО поколения тоже будет взят, то есть оценка завышается — ратифицированное направление. Посадка `FC8-free-call-dropped-from-a-paid-generation`. -- **[LOW→взято] `$0`-строка, чей новейший вызов чужой,** котировалась `$0` без оговорки: переполнения на нуле не бывает, поэтому детектор отката её не видел. Теперь такая строка помечается «не удалось пере-оценить»; `skipped`-строка остаётся честно бесплатной. Посадка `FC8-unestablished-zero-row-published-as-free`. ⚠ Существующий случай таблицы, чей комментарий говорил «skipped-строка», диспозицию не ставил — привёл тест к его собственному замыслу и добавил случай без скипа (объявляю по D39.183: поведение сменилось по находке, тест стал строже). -- **[LOW→взято] развилка ЛОГА из Б1 не была прибита** — мутация «удалить ветку конца книги» переживала весь пакет. Новый `TestTheRunLogSaysWhichKindOfVolumeStopItWas` (обе ветки, живым прогоном), посадка `FC8-log-fork-at-the-end-of-the-book`. -- **[MED→исправлено] отчёт пака называл пять посадок разведочными именами** (`Y2…`, `Y3`, `Z2`, `X29`, `Y4`), которых в каталоге нет: Б7 завёл их под именами `FC7-*`, но ссылки остались старыми. Переименованы; `grep` по каталогу на старые имена → 0, ни одно из них больше не стоит в отчёте как имя гейта. - -**Второй проход того же проверяющего — по трём осям, вердикт его словами.** Дерево ему было пере-сдано после правок; он адресно пере-атаковал свои находки 1, 3 и 6 (все три ДЕРЖАТ, каталожные посадки красные), отдельно проверил новое правило обхода `usd()` таблицей краевых форм (откат, остаток, эскалационный хоп, `skipped`, нулевая стоимость) и занижений не нашёл. **A — исходный промт: исполнено с остатком** (остатки — два схемных остатка строки 232 и отложенный вопрос владельца, все названы и заказаны такими). **B — наряд Б1–Б9: все девять закрыты** (Б8 был закрыт с остатком, см. N1 ниже — закрыт). **C — код и отчёты: ГОДНО К ПРИЁМКЕ, блокеров нет.** Три его новые находки, все LOW, закрыты здесь же: -- **N1** — строка отчёта говорила «18 заведено дофиксом и проверкой», а раскладка в скобке давала 14 и не называла `FC1`. Правда: прирост 18 = паком 6 (`FC1`×4, `FC5`×2) + дофиксом и разбором проверки 12 (`FC6`×4, `FC7`×5, `FC8`×3). Тот же класс, что чинит Б8, — число одной величины под именем другой; пере-считано командой, она стоит рядом. -- **N2** — условие `len(calls) > 0` в метке «поколение не установлено» не было прибито: мутация переживала пакет. Добавлен случай «`$0`-строка без единого вызова на файле свободна, а не нераскрыта» + запись `FC8-empty-cell-disclosed-as-unestablished` (красная). -- **N3** — комментарий у той же ветки описывал `skipped`-строку так, будто guard несущий, тогда как продовые вызывающие skipped-строки сюда не доводят (`projectRebill` их отфильтровывает, `projectBookUSD` роняет флаг). Комментарий говорит теперь ровно это: guard защитный, а не несущий, и почему он остаётся. -Его диспозицию по находкам 4 и 5 («согласен, входы в отложенный вопрос владельца») принимаю; он же заметил, что находку 4 можно закрыть и без смены семантики — снимком plan-time `Carried`. Это ВЫБОР, не необходимость, и он входит в тот же вопрос владельца. - -**Что НЕ удалось и что НЕ проверено (дофикс).** -- **Цикл из трёх покупок на флагнутом юните** не замерен — см. Б10. Знаю, что не проверено; не чинил, потому что наряд прямо запретил трогать семантику переноса. -- **Состояние `Delivered == Carried` с исходом класса `unitFresh`** (Б3а) ни одной фикстурой не достигается; инвариант утверждён на сконструированном состоянии, и это сказано в самом тесте. -- **Живого прогона с платными вызовами не было** ни в паке, ни в дофиксе. -- **Оба остатка строки 232** (первая редакторская стадия; прерванная пере-делка) по-прежнему открыты и требуют схемного носителя. -- **Четыре находки проверяющего названы, но НЕ чинятся — все четыре суть входы в отложенный владельцем вопрос о семантике переноса, и наряд прямо запретил её трогать:** - 1. Клауза «finished OUTSIDE this grant» ключуется на `Carried` ПОСЛЕ `reconcile`, поэтому исчезает, когда все перенесённые юниты флагнулись: оператор видит «2 PAID FOR BUT FLAGGED», но не то, что обе оплаты шли вне гранта. Воспроизведено: `VolumeStop{MaxUnits:2, Delivered:2, Carried:2}` + два флага класса `unitCarried`. - 2. Перенесённый юнит с ПРОТУХШИМИ черновыми строками пере-покупает всю цепочку вне гранта, тогда как хелп флага обещает «дописать недостающее». Воспроизведено проверяющим: `granted()=0`, два вызова провайдера на один юнит. - 3. Подсказка в докстринге `Delivered` ключуется на `delivered > max_units` и молчит в форме «весь остаток перенесён», где `delivered == max_units` (это же Б10 наряда). - 4. Цикл из ТРЁХ последовательных покупок на флагнутом юните не гонял никто — ни приёмка, ни проверяющий, ни я. -- **Выживание трёх гарантий ФЧ-1 на дереве ДО пака** проверяющий сам не воспроизводил (принял по моим красным посадкам); я это делал в паке отдельно, лог в scratch-каталоге сессии. - -### 05.09 — ОТЧЁТ пака «денежный стоп перестаёт быть аварией» (сессия `textmachine-main-be`, промт `BACKEND_MONEYSTOP_SESSION_PROMPT.md`) - -> **ПОЛНЫЙ ОТЧЁТ — `backend/docs/MONEYSTOP_REPORT.md`** (происхождение решений, пять рисков зоны по -> одному, самопроверка, объявления по D39.183, obstacle reporting). Здесь — итог и то, что нужно -> оркестратору для лендинга. **Дерево НЕ закоммичено.** -> -> ⚠ **В дереве лежат ЧУЖИЕ правки — вся `platform/`** (параллельный пак `textmachine-main-63`). Не -> тронуты ни байтом. **Платного прогона НЕТ**: все три оси предъявлены за $0. - -**Четыре пункта закрыты.** (1) строка **271** — правки банка больше не могут писать свои строки внутрь -system-сообщения платного вызова: одна граница (`internal/membank/wirefence.go`), три точки исполнения — -дверь отказывает, загрузчик сида отказывает, рендереры пропускают; предел 200 рун, запрет Cc + U+2028/9 + -bidi-переопределений (Trojan Source), при этом ZWJ/ZWNJ/LRM/RLM РАЗРЕШЕНЫ (общность §0.1); фолд версии -СКОУПНЫЙ, чистая книга хеширует байт-в-байт как до фикса — запинено значением, снятым сборкой `32be78a`. -(2) строка **277** — отказ резерва защёлкивает волну и не отменяет допущенное; отказ не окончателен при -непустом полёте (ждём settle, повторяем тот же резерв); только ОБЯЗАТЕЛЬНЫЕ вызовы ждут. (3) строка -**278** (событие) — недостача едет в `ceiling`, `StreamVersion` 1.2 → 1.3, денежный леджер в `finished`. -(4) строка **278** (проекция) — цена книги ДО покупки в `manifest --json` и `status --json`. - -**Числа, каждое получено командой (полный список — §6.7 отчёта).** ⚠⚠ **ТРИ ИЗ НИХ ПРОТУХЛИ ЧЕРЕЗ ТРИ ЧАСА, И ОДНО ПЕРЕВЁРНУТО — поправка в конце абзаца; она найдена ВЕРИФИКАТОРОМ 05.09, не зоной и не мной:** тестовых функций **1172 → 1208 (+36)**, все тридцать шесть в трёх новых файлах; `git diff -U0 -- backend | grep -E '^[+-]func Test'` ПУСТ — -ни одна существующая не удалена и не переименована, два протухших починены внутри тела. Каталог мутаций **158 → 191**. Батарея: `MAKE_EXIT=0`, 19 пакетов, 0 упавших, 0 issues (шестой прогон — первые пять красны или остановлены). Каталог ЦЕЛИКОМ: **191 запись, 187 RED, 0 SURVIVED, 0 NOTHING, 0 ROTTED, 0 неожиданных исходов** — второй полный прогон; ПЕРВЫЙ не сошёлся и нашёл четыре дефекта в моих же артефактах, включая named-артефакт приёмки (§6.5 отчёта) (и `go vet -vettool` -собственного анализатора зоны — чисто; он поймал у меня один хенд-билд `config.Stage`, см. отчёт). - -⚠⚠ **ПОПРАВКА 05.09 (замер верификатора по дереву на `d766007`; числа выше НЕ переписаны — они были верны в свою минуту).** Абзац заленджен коммитом `ba4fd34` через **девять секунд** после пака `81a89e9` и за **три с лишним часа ДО** дофикса `616a8e4`, который добавил ещё +5 тест-функций и +10 записей каталога. Сообщение коммита подписало их «final counts», и после дофикса их никто не тронул. Верно на сегодня: тестовых функций **1172 → 1213 (+41)** · каталог мутаций **158 → 201** · каталог целиком — **201 запись, 197 RED, ЧЕТЫРЕ аргументированных выживших, 0 неожиданных исходов**. ⛔ **⚠ **Испр. 06.09 автором находки: ПРОТУХЛИ ДВА числа, не три, и „0 SURVIVED" НЕ протухло.** У инструмента ДВА ведра: `SURVIVED` — **неожиданный** выживший (настоящая дыра), `survives` строчными — записанный и аргументированный. На ревизии, которую абзац описывает, каталог был 191 = 187 red + 4 survives, то есть все три числа верны ДЛЯ СВОЕЙ РЕВИЗИИ; четыре аргументированных были четырьмя уже тогда. Протухли 191→201 и 187→197. Третье — не протухание, а НЕПОЛНОЕ ПЕРЕЧИСЛЕНИЕ: 187+0+0+0 = 187 при собственном итоге 191, потому что ведро аргументированных не названо. Дефект другой природы и слабее. Прежняя редакция: **Переворачивается именно последнее: «0 SURVIVED» против четырёх записанных выживших** — а это то самое различение из `D39.202` п.3 (`SURVIVED` как СООБЩЕНИЕ О НЕДОСТИЖИМОСТИ, а не как провал), и следующая движковая смена открывает этот журнал РАНЬШЕ акта. Акт `D39.206` всё это время нёс верные числа — разошлись НОСИТЕЛИ, а не факт: класс «поправил читателя, оставил писателя» (`D39.202` п.1). - -⛔ **ТРИ ВЕЩИ, КОТОРЫЕ ОРКЕСТРАТОРУ НАДО ЗНАТЬ ДЛЯ ЛЕНДИНГА:** -1. **Наружу едет НЕ то число, что назвал промт, и это согласовано.** §4(3) назвал `denied estimate` и - следующей фразой запретил его класс («цены вызовов не выходят»). Едет **недостача** - `committed+reserved+estimate−ceiling`, поле `shortfall_micro_usd` (строка 278 называла - `next_step_micro_usd`). Две оговорки в доке поля: при непустом полёте это ВЕРХНЯЯ граница; для - `day`-скоупа поле ОПУЩЕНО (дневной потолок суммирует все книги стора, движковые цифры — одной). -2. **Третье лечение пункта 1 («обрамление как ДАННЫХ») в ТЕКСТОВОЙ форме невыполнимо.** - `lang/data/injection.txt` побайтно входит в `EmbeddedVersion()` → `cutTag` → **внутрь каждого - `unit.id`**: добавление одной строки пере-чеканило бы id всех книг. Сделано структурно. -3. **`res.Volume` при денежном стопе НЕ прикладывается.** Семь счётчиков `VolumeStop` — план; денежный - стоп оставляет допущенные-и-не-доделанные юниты посчитанными как `Delivered`, а `LeftFresh` заниженным - на них, и при `Left()==0` прогон, вставший посреди книги, объявил бы «дошёл до конца». Восьмого бакета - нет ⇒ у денежного стопа СВОЙ леджер `Finished.Money{units_resolved, units_deferred}` из тех же - счётчиков, что и `progress`. - -⚠ **ЧЕТВЁРТОЕ ДЛЯ ЛЕНДИНГА, и это вход в СТРОКУ 283, а не мой дефект: `structure: detected` сегодня -CJK-ONLY.** Сопоставление заголовков в интейке идёт по `lang.DefaultCJKSection()` — КОНСТАНТА (章节節回 + -класс CJK-числительных), не данные пары. ⇒ книга в любой другой письменности возвращает `none`, как бы -явно её главы ни были размечены. Асимметрию пак НЕ вносит — движок уже читает данные пары для РЕНДЕРА -заголовка (`configs/langpacks/*/heading.txt`), а РАЗРЕЗ остаётся захардкоженным, и строка **283** называет -именно этот раскол работой. Что поле ДОБАВЛЯЕТ: у асимметрии появляется имя на проводе вместо того, чтобы -прятаться в числе глав — не-CJK книга честно говорит `none`, и форма заказа (D39.196) не предложит -ползунок в ГЛАВАХ против разреза, которого никто не делал. Классификация сама пар-агностична (спрашивает, -какой ПУТЬ провёл границы) и скажет `detected` без правки Go в день, когда разрез научится читать пару. -⚠ Первую редакцию комментария и отчёта я написала как «пар-агностично по построению» — это было -пере-утверждение, поймано собственным проходом и исправлено в обоих местах. - -**ПРАВКИ ЧУЖИХ ТЕСТОВ — объявление по D39.183 (все три вызваны сменой поведения, заказанной паком):** -· `runevents_test.go` — пин «the ceiling event must carry the fact and no figures» СУЖЕН, не снят: белый -список ключей (`halted|scope|shortfall_micro_usd`), запрет любого `$`, ПЛЮС новое утверждение — book-стоп -обязан назвать недостачу, day-стоп обязан её не называть. ⚠ Фраза ратифицирована (D39.106 §2/ПТ-33) — -ноту о сужении берёт на себя оркестратор. -· `wavepanic_test.go`, `volumepanic_test.go` — оба ВИСЛИ (`panic: test timed out after 1m30s`): парковали -падающий воркер на `<-ctx.Done()`, которую вызывал ПОТОЛОК. Потолок больше не отменяет — это заказ пака. -Триггер заменён, утверждения не ослаблены. - -**ПРАВКА ВНЕ ЗАКАЗА, взятая по своему усмотрению:** течь резервации на ветке `json.Marshal(resp.Usage)` -(`stagerun.go`) — возврат без release и без settle. Довод: денежный путь, и у него появился второй -читатель — утёкшая резервация ЗАВЫШАЕТ публикуемую недостачу. Ветка практически недостижима, поэтому её -посадка занесена аргументированным выжившим, а не находкой. - -⚠ **ЧЕТВЁРТЫЙ КЛАСС ЛОЖНОЙ ЗЕЛЕНИ, найденный в этой смене (общий, не про мой пак):** `t.Fatalf` из -ПОРОЖДЁННОЙ горутины — `runtime.Goexit` не на той горутине: утверждение теряется, прогон висит до -таймаута ПАКЕТА. **Посадка, чей отказ выглядит как таймаут, неотличима от нагрузки и потому не является -посадкой** — а `Makefile` зоны сам документирует разброс `internal/pipeline` 95 → 560 с под параллельными -сессиями. Рядом с тремя известными: упавшая сборка, вырожденное утверждение, эквивалентная посадка. - -**ГОТОВЫЙ ТЕКСТ СТРОКИ БЭКЛОГА** (таблица — зона оркестратора, поэтому текст лежит здесь, а заводит его -он; уже принят и заведён как строка **291**): - -> **Отказ РЕЗЕРВАЦИИ на ретрае не деградирует и не оставляет следа — юнит может заклинить навсегда, а -> волна не может продолжиться без дыры.** `maxTokensForAttempt` удваивает бюджет на каждой регенерации -> (`disposition.go:398`) ⇒ attempt 1 резервирует вдвое больше attempt 0. Отказ ретрая уходит наверх из -> цикла попыток (`stagerun.go:164-167`) РАНЬШЕ `UpsertChunkStatus` (`stagerun.go:285`) ⇒ чекпойнт -> attempt 0 есть (реплей за $0), строки `chunk_status` нет, читающие модели видят `pending`. Следствия -> два: (1) если потолок покупки не вмещает attempt 1 — юнит не доделается НИКОГДА, каждый резюм реплеит -> attempt 0 за $0 и умирает на attempt 1, прогон уходит exit 4 при полностью доставленных купленных -> юнитах; (2) продолжить волну после отказа ретрая нельзя без дыры в непрерывном префиксе — то есть цена -> глобальной защёлки (недодача юнитов, которые ВЛЕЗАЛИ; разброс резервов внутри волны замерен 2.976×, -> разбор 04.09) не снимается различителем «свежий/ретрай». Два пути, ВЫБОР НЕ СДЕЛАН: (а) отказ ретрая -> деградирует во флаг attempt 0 — смена D2.3, закрывает оба следствия; (б) минимальная покупка платформы -> ≥ `step_max_usd` движка — закрывает только (1) и стоит продукту минимальной покупки ~5 глав | -> бэкенд+платформа | скоро | выбор владельца между (а) и (б) + пин | бэкенд-сессия денежного стопа 05.09 - -**ГДЕ ПРОМТ НАПИСАН НЕ ТАК** (требование §5-тер(а), полный список — §5 отчёта): §4(3) внутренне -противоречив (см. выше) · §4(1) заказывает лечение, невыполнимое без нарушения §4(6) · §3 п.1 называет -секцию `15-money-path.md`, переименованную 05.09 · §4(2) обосновывает защёлку доводом, который снят самой -конструкцией (`haltErr` — отдельный слот, путь ошибки один при любом W); защёлка оставлена по ДРУГОМУ -доводу — продуктовому · §4-тер риск (б) назван мягче, чем он есть: счётчики не «получают новую форму», а -врут по шести полям из семи. +## Отчёты пака «закон банка честно» и петли `D39.191` — ВЫНЕСЕНЫ В АРХИВ 11.09 + +> Хроника закрытых пунктов — **строки 210 · 113→46 · 134 · 140 · 184(а)** и дофикс чужого пака (номера названы +> здесь намеренно: греп по номеру строки обязан приземляться на указатель, а не мимо), разметка свободы, самопроверка, «что не удалось», +> дофикс приёмки 04.09, круги 2 и 3 петли `D39.191`) уехала в +> [`archive/PROGRESS-2026-09-04-07-bank-law.md`](archive/PROGRESS-2026-09-04-07-bank-law.md) — 196 КБ, 14 секций. +> Вынесено по мандату владельца на снос протухшей прозы (`D39.239` п.4); ссылок извне на блок было **0**. +> Живое из этих отчётов живёт нотами и строками трекера, а не здесь. ## Полигон diff --git a/docs/archive/PROGRESS-2026-09-04-07-bank-law.md b/docs/archive/PROGRESS-2026-09-04-07-bank-law.md new file mode 100644 index 00000000..aa50fa99 --- /dev/null +++ b/docs/archive/PROGRESS-2026-09-04-07-bank-law.md @@ -0,0 +1,608 @@ +# Архив хроники — отчёты пака «ЗАКОН БАНКА ЧЕСТНО» и петли `D39.191` (закрыты 04–07.09) + +> ⚠ **АРХИВНЫЙ СРЕЗ. При онбординге НЕ читать — только по конкретной ссылке.** Вынесено из живого +> `docs/PROGRESS.md` 11.09 по мандату владельца на снос протухшей прозы (`D39.239` п.4). Здесь лежат +> отчёты ЗАКРЫТЫХ пунктов: шесть пунктов наряда (строки 210 · 113→46 · 134 · 140 · 184(а) и дофикс +> чужого пака), разметка свободы, самопроверка, «что не удалось», дофикс по приёмке 04.09 и два круга +> петли `D39.191` — второй не замкнулся, третий замкнулся по содержанию, но не по условию. +> ⛔ **Ничего отсюда не исполняется.** Всё живое из этих отчётов ратифицировано нотами и заведено +> строками трекера; сюда вынесена ХРОНИКА, а не долг. Проверено перед выносом: ссылок на этот блок +> извне живого журнала — **0** (контроль: якорей вида `PROGRESS.md:NNN` в живых доках 17, ни один не +> указывает в этот диапазон; секции по имени вне журнала — 0 хитов на каждую из четырёх проверенных). + +## Пункт 1 — строка 210: род ПРОИЗВОДИТСЯ. ЗАКРЫТ, с честной границей доставки + +**Развилка выбрана ЧЕТВЁРТАЯ, которой в промте не было: род производит существующий фокус-проход КЛАССИФИКАТОРА** (`runClassifier`), третьим полем ответа. Довод: ноль новых вызовов (проход уже гоняется по тем же кандидатам с теми же контекстами) при цели №5 «низкий COGS»; род — свойство сущности, выводимое из контекстов, то есть акт классификатора, а не выбор рендеринга; парсер классификатора позиционный и закрыто-словарный, тогда как третье поле терминолога — уверенность, вокруг которой написана деликатная развязка мис-сплита по живому дефекту «наставник 95%». Цена: двигается `RequestHash` ТОЛЬКО классификаторных вызовов; терминологические чекпоинты книг живы. Ратифицировано оркестратором. + +**Построено.** Промпт пары `prompts/zh-ru/classifier.md` v2 — третье поле, закрытый словарь `male|female|neuter|none`. ⚠ `hidden` в словарь НЕ входит НАМЕРЕННО: «пол скрыт до раскрытия» — решение о сюжете и спойлерной политике книги, а не наблюдение по KWIC-строкам; ошибочная догадка де-гендерила бы обычного персонажа на всю книгу. Остаётся ручным, в сиде. `none` — полноценный ОТВЕТ, а не пропуск колонки: без слова для «рода нет» единственный способ это сказать — не писать колонку, что неотличимо от «модель перестала отвечать». +Go: `terminology.Genders` + `ParseTypes` возвращает `types, genders, stats`; `ReplyStats.BadGender`/`NoGender` — два счётчика, потому что это два разных отказа; `pipeline.attachClassifiedGender`; `miner.Term.Gender` → `DeltaFile` → сид-YAML → сайдкары → банк. +**Гарантия, которую держит тест:** колонка рода НИКОГДА не стоит классу — значение вне словаря отбрасывается и считается, класс сохраняется (`TestTheGenderColumnNeverCostsTheClass`). + +**⛔ ГЛАВНАЯ НАХОДКА ПАКА: ЗАМКОВ ТРИ, И НАЗВАННЫЙ В ПРОМТЕ — НЕ СВЯЗЫВАЮЩИЙ.** +`bankmaterialize.go` держит ДВА банка: черновая волна выбирает по `baseMemory`, из которого строки `Source:"mined"` вычеркнуты ПО ПОСТРОЕНИЮ, редактор видит обогащённый. Авто-банк пишется как `mined` (`mining.go:671`), подписанная дельта — тоже (`mining.go:896`, дословно «override the seed loader's Source:seed → mined (base-excluded)»). ⇒ **на ЧЕРНОВОЙ провод произведённый род не едет и не поедет: там нет самой СТРОКИ.** Это не дефект — это денежный контракт `D39.42` п.3: инъекция черновой волны байт-стабильна поперёк обогащения банка, иначе пере-снапшот платит две волны вместо одной. Не трогал и не предлагаю. +**Формулируй границу так и не короче:** род производится авто-путём и доезжает до РЕДАКТОРСКОГО провода в том же прогоне, что его произвёл; на ЧЕРНОВОЙ не едет, пока цена двух волн не решена отдельно. Не «род построен». + +**Второй замок СНЯТ по ратификации оркестратора, и снят РАСЩЕПЛЕНИЕМ, а не целиком.** `Disp` сворачивал два вопроса — СТАТУС строки и надёжность МАТЧА. `D39.104` п.1 упраздняет разнобой по статусу («банк на проводе = закон независимо от статуса строки»), но про матч там нет ни слова и быть не может: род, приписанный не той сущности, — ошибка, а не неподписанность. Заведено поле `PickedEntry.KeyTrusted` + тип `InjectionTrust{Disp, KeyTrusted}`, который несёт ОБА факта через липкое окно (у липкого переноса нет своего ключа, и матч-половину, как и диспозицию, восстановить нечем). Род теперь гейтится `KeyTrusted` на ОБОИХ блоках; статус не решает ничего. +**Сквозное доказательство — три двери порознь, а не «работает»** (`internal/pipeline/gender_wire_test.go`): (1) неподписанная строка на доверенном ключе НЕСЁТ род редактору в том же прогоне; (2) collision-prone матч не несёт НИКУДА, при ЛЮБОМ статусе (юнит-проверка: на zh ветка не срабатывает физически, ханьские ключи якорятся на 2, поэтому сквозной zh-фикстурой её достать нельзя); (3) на черновой провод род не попадает НИКОГДА — и утверждение перевёрнуто нарочно: откроет кто-то дверь 3 — тест покраснеет и заставит автора назвать, какую именно. +**Отрицательный контроль есть:** `TestASignedGenderBeatsTheProducedOne` — сид владельца побеждает, и не порядком присваивания, а тем, что `loadAutoBank` роняет предложение движка на занятый подписью ключ. +Команда: `go test ./internal/pipeline/ -run 'Gender' -v`. + +⚠ **Пункт 4 — ПРЕДПОСЫЛКА пункта 1 в бою.** Род производится только когда включён `classify_types`. Без банкового контура в боевом конфиге род не производится ни при каком варианте развилки. + +## Пункт 2 — строки 113 → 46: база записана, экран построен. ЗАКРЫТ + +**113 — СТОП СНЯТ, все три порога пройдены.** Корпус взят исполнением: все чекпоинты всех 162 БД стенда, через СОБСТВЕННУЮ предобработку движка (`splitBanknote` → `checks.StripThink` — тот текст, который видит гейт), дедуп по хешу, только отгрузочные роли (банк-роли исключены: их ответы законно состоят из исходных термов, и включив их, мерили бы исключение, а не гейт). Обход дал **1162 ответа: 30 off-target, 1132 здоровых** — но ⚠ **в РЕПОЗИТОРИЙ уехала ВЫБОРКА, и все числа ниже воспроизводимы над ней, а не над обходом**: 74 строки, все 30 off-target плюс 44 ЗДОРОВЫХ, отобранных нарочно трудными (20 с высшей долей исходного письма, 15 с низшей долей целевого, 3 под полом улики, остальное — спред). Тексты в git не поехали, довод — в шапке `offtarget_corpus_test.go`. +**Разметка НЕ гейтом, иначе она была бы пересказом вердикта:** off-target = доля КИРИЛЛИЧЕСКИХ букв < 0.5 — свойство ЦЕЛЕВОЙ стороны, независимое от проверяемого исходно-письменного гейта. Корпус разделяется полностью: здоровые все выше 0.9, off-target все ровно 0.0000, полоса между ними ПУСТА. +**Числа воспроизведены и совпали с опубликованными до знака:** худший здоровый source-share **0.011655** (контракт называл 0.0117), запас **12.87×** против порога 0.15 (контракт называл 13×), **ноль ложных на 44 трудных здоровых** (не на 1132 — над репозиторием проверяемо именно это), поймано **28 из 30** (контракт называл 18 из 20 на более узком срезе того же стенда — та же доля, те же два промаха). Промахи — два беглых АНГЛИЙСКИХ черновика на **4690** и **4811** букв, ровно те, что названы в `EFFORT_HANDLE §3.1`. Худшая здоровая доля ЦЕЛЕВОГО письма **0.9767**, максимум у off-target **0.0000**, самый короткий off-target **346** букв — все три числа контракта воспроизведены. +Команда: `go test ./internal/pipeline/ -run 'OffTargetCorpus|LiveEchoGate|LatinOffTarget' -v`. + +⚠ **ЧЕГО ФИКСТУРА НЕ НЕСЁТ, и это мой выбор с ценой.** `testdata/offtarget-corpus.json` несёт РУНОСЧЁТ каждого образца (всего рун, рун исходного письма, букв, букв целевого письма), происхождение, метку и шестидесятирунную голову для человека — но не тексты. Тексты — копирайтная вебновелла и её машинные переводы, которые репозиторий держит вне git намеренно (стенд версионируется отдельным репо, `example/` возит собственный фрагмент по той же причине). Руносчёта достаточно ПО ПОСТРОЕНИЮ: обе проверяемые функции — отношения классов рун и ничего больше не читают, и тесты кормят НАСТОЯЩИЕ функции скрипт-верным суррогатом, собранным из счётчиков, а не считают арифметику вместо них. **Названная потеря:** детектор, который будет читать слова, n-граммы или орфографию, на этой фикстуре не измеряется — ему нужен повторный обход стенда. + +**46 — экран построен формой (а) плана §5.4 и заморозкой `D39.92`/`D39.93`.** Отдельный пакет чистых функций `internal/langscreen` (вход: текст + скрипты цели; выход: ИМЕНОВАННЫЙ вердикт `on_target|off_target|abstain` + счёт и объём улики). Порог `OnTargetFloor = 0.50` — КОНСТАНТА в пустом интервале, не тюнинг (гарантия Неймана–Пирсона на этом корпусе не покупается — `D39.92` п.1а); пол улики `MinLetters = 200` калиброван, а не выбран: самый короткий off-target 346 букв, ниже пола падают три здоровых. Носитель данных — `lang.LangScripts`, НОВОГО эмбед-файла не заведено. +**Флаг СВОЙ — `FlagOffTargetLang`, `FlagCJKArtifact` НЕ переиспользован** (`D39.93` п.2), явный вход в `escalatable()`, не `retryable`. `classifierVersion` v2 → v3, и версия правила `langscreen.Version` вплетена в литерал, так что правка ВНУТРИ модуля двигает снапшот сама, а не по дисциплине. +**Приёмочный критерий §5.4 шага 5 исполнен:** офлайн-прогон по корпусу — экран новооткрывает РОВНО два ответа, оба английские, здоровых не двигает ни одного (в тесте это `Fatal`, а не `Error`: план требует СТОП). Вдвоём два правила ловят **30/30 при нуле ложных**. +Команды: `go test ./internal/langscreen/ -v` · `go test ./internal/pipeline/ -run 'TargetScreen|TwoScreens|BankRoleReply' -v`. + +**Вторая половина дыры закрыта тем же диффом:** `detectLatinInsertion` теперь под `c.TargetScriptNonLatin()` (`internal/checks/sanitizer.go`). Правило существовало в прозе доккоммента и стояло только на пути репэйра; первая же латинописьменная цель уронила бы КАЖДЫЙ чанк чистой целевой прозы как «Latin insertion». Для ru вердикты не двигаются ни на байт (предикат истинен) ⇒ бампа версии санитайзера не требуется. + +⚠ **НАХОДКА, приехавшая ВМЕСТЕ с паком, а не до него.** Экран и эхо-гейт обязаны молчать на ответах банк-ролей: их буквы — движковые идентификаторы и исходные термы, поэтому эхо-гейт флагал бы их (и флагал: `verdict=cjk_artifact` при `bad_lines=0` на чистой пробе 6/6), а экран флагал бы ВТОРЫМ способом. Исключение было записано как `role == roleTerminologist` и КЛАССИФИКАТОРА не покрывало (`13-tech-debt-anchors §Б-105`) — латентно, пока ни один боевой конфиг классификатор не включал. Пункт 4 его включает. Поле переименовано в `NonProseReply` (оно про ФОРМУ ответа), предикат один — `isBankRole`, рядом со строкой, которую новая банк-роль и так обязана добавить в `bankRoleSettings`. + +## Пункт 3 — строка 134: рендер снят, данные не тронуты. ЗАКРЫТ + +**Выбор объявлен: снят РЕНДЕР в Go.** Механизм, по которому выбор сделан: `cutTag` сворачивает `lang.EmbeddedVersion()`, `injection.txt` лежит в `internal/lang/data/` ⇒ правка данных пере-чеканила бы `unit.id` всей книги → `manifest_key` → снос `unit_resolutions` у платформы (строка 198). `RenderFormatVersion` в `cutTag` НЕ входит — он фолдится только в снапшот. Предъявление — первой строкой этого отчёта. +**Текст заголовка не менялся вовсе,** и это проверено, а не принято на веру: действующий `editor_header` БАЙТ-ИДЕНТИЧЕН арму `AM-noclause` пробы 18 (`experiments/18-editor-wire-probe.md` §0.3 дословно: «Закон БЕЗ оговорки = действующий `editor_header` без единой правки»). Оговорка области НЕ внесена (опровергнута §A.4). +**Что изменилось:** маркер ⟨проверить⟩ не рендерится НИ В ОДНОМ блоке; секция `editor_unverified_header` не рендерится, две секции редактора схлопнуты в один закон-блок; неподписанные строки стоят в нём под тем же заголовком и ничем не помечены. Статусы строк и подписная таблица не тронуты (`D39.104` п.1). `RenderFormatVersion` v3 → v4. +**Ревью-ось «маркер снят, а пост-чек жив» проверена:** `membank.Postcheck` ключуется на `p.Disp`, маркера не читает — обработка `Ambiguous` цела, счётчики `Shown`/`Followed` живы. ⚠ Но их СМЫСЛ сузился, и это записано в код: они родились на проводе, который помечал строку и приглашал модель её отвергнуть; теперь неподписанная строка показана законом, отклонение — просто отклонение, а не «модель пользуется дарованным правом». Та же поправка внесена в доккоммент миграции v12, которая эти колонки завела. +**Операторские тексты, которые стали бы ложью, переписаны** (не комментарии — то, что печатается человеку): две строки `tmctl` про «уедет к редактору с пометкой ⟨проверить⟩», строка `Unsigned bank terms` в `report`, WARN стопа майнинга, WARN конфликта с подписанным банком. + +⚠ **НАХОДКА, которую я НЕ ЧИНИЛ и которая теперь висит на строке 250** (носитель заведён оркестратором): строка данных `ru glossary_header` САМА ОПИСЫВАЕТ снятый маркер («строки с пометкой ⟨проверить⟩ — неподтверждённые кандидаты»). После пака помеченных строк на проводе нет, а заголовок продолжает объяснять модели пометку. Это не мёртвая строка данных (`editor_unverified_header` и `unverified_marker` — те просто перестают читаться, и обе оставлены ОБЪЯВЛЕННЫМИ полями, потому что их СТРОКИ остаются в эмбеде, а `parseInjection` отвергает незнакомый ключ), а ЖИВАЯ строка с висячей ссылкой. Чинится только правкой `injection.txt`, то есть ценой `cutTag`. По `D39.104` п.1 клаузула становится ВАКУУМНОЙ, а не ложной; вред не измерен. + +## Пункт 4 — строка 140: контур внесён в c1. ЗАКРЫТ с НАЗВАННЫМ предусловием развёртывания + +**Внесено в `configs/pipeline-c1.yaml`:** `gates.banknote.enabled: true` + `prompt_override: ../prompts/zh-ru/translator-banknote.md` на стадии draft со своим лейблом (`D39.10`: включение банкноты — ДВА согласованных изменения; сам гейт только СРЕЗАЕТ блок, инструкцию писать его несёт промпт) · `gates.terminology` (модель `deepseek-v4-flash`, `reasoning: low` — у рендер-фазы это вынужденно, на вендорском дефолте она сжигает бюджет размышлением и возвращает пустое тело) · `classify_types: true` · `mining.contrast_path`. + +**Суб-бюджеты — числа и их довод.** Множитель ×4.47 чинит пере-пин цен DeepSeek и ТОЛЬКО его; прежние 0.05/0.02 размерены под десятиглавую пробу, а этот файл обслуживает книги целиком. Замер (строка 16, десять глав, июльские деньги ×4.47): терминолог **$0.0257**, классификатор **$0.0344**. Бюджеты КНИГО-ШИРОКИЕ и копятся по всем прогонам книги (`store.RoleSpentUSD` суммирует чекпоинты роли по всем её джобам), а кандидатное пространство терминолога зависит от того, какие чанки уже отчерновлены, — значит между покупками оно меняется, `request_hash` другой, и книга, проданная десятью покупками, платит десять книжных консолидаций. Пол ⇒ **0.257 / 0.344**. Взято **1.00 / 1.00**, запас **3.9× / 2.9×**. +⚠ **Первая редакция дала классификатору 0.50, то есть запас 1.45×, а комментарий рядом обещал «≈3×» — число одной величины под именем другой. Поймано МОИМ ЖЕ адверсариальным проходом, исправлено: по замеру классификатор ДОРОЖЕ рендера, и «половина» пришла из старых чисел, а не из измерения.** Бюджеты остаются раздельными при равных значениях: разделение про то, чтобы перерасход одной фазы не голодал другую. +Пин: `TestTheBankRoleBudgetsClearTheirMeasuredCost` — это ПОЛ, а не значение; поднимать можно свободно, опускать ниже замеренного нельзя. + +**⛔ ПРЕДУСЛОВИЕ РАЗВЁРТЫВАНИЯ, названное вслух: `mining.contrast_path` — единственный вход контура, которого нет в git.** ОБА банк-роля живут ВНУТРИ стопа майнинга, и `runBankMiningStop` возвращается РАНЬШЕ построения хоть одного кандидата без корпуса контраста. Артефакт (jieba-словарь общей частотности, 5 МБ) под `.gitignore` намеренно, но ВОСПРОИЗВОДИМ — рецепт и sha256 запинены в `docs/experiments/16-bank-mining.md`. Носитель требования к рантбуку и слово оператора — строка **251** (заведена оркестратором). + +**Два гарда, которые делают «настроено и мертво» невозможным** — и оба нашлись не по заказу, а по разбору: +1. Загрузка отказывает, если гейт включён, а `mining.contrast_path` ПУСТ (`internal/config/pipeline.go`) — тот же класс «гейт, который не может выстрелить», который лоадер ловит везде. +2. Наличие ФАЙЛА проверяется на ПИШУЩЕМ пути, рядом с `CheckKeys` (`Pipeline.CheckMiningContrast`, вызов в `openRunner` внутри `forWrite`). Место выбрано не случайно: `os.Open` внутри прогона стоит ПОСЛЕ оплаченной черновой волны, то есть развёртывание, забывшее артефакт, платило бы за волну, чтобы это узнать. $0-поверхности не трогаются (`D20.4`). +3. ⚠ **ТРЕТИЙ гард добавлен по находке адверсариального прохода, и он закрывает дыру в моих же первых двух.** У раннего возврата ДВА условия, и я гейтил только одно: `r.pack == nil` — а нил-пак книга получает, если не объявила `langpack_root` вовсе или объявила корень без каталога своей пары («nil-and-run»). Такая книга могла нести полностью настроенный и забюджетированный контур и не исполнить из него ни строки. Конфиг-лоадер этого структурно не видит: гейт знает пайплайн, пак знает книга, и вместе они есть только в раннере. Отказ добавлен там, тоже только на пишущем пути. + +## Пункт 5 — строка 184(а): жанр выкинут. ЗАКРЫТ + +`grep -rl '{{genre}}' backend/prompts/ | wc -l` → **6 → 0**. Поле снято из канона брифа (`BriefHash`), `{{genre}}` снят из плейсхолдеров `pipeline.Render` — значит промпт-пакет пары, написанный позже с этим маркером, падает ГРОМКО на первом рендере, а не подставляет пустоту. Репо-гейт на ту же вещь: `TestNoShippingPromptAsksForTheGenre`. +**`BriefHash` двинулся у ВСЕХ книг** (`c2021b1b…` → `4624529b…` на голден-фикстуре) — это заказано `D39.138` п.2в и внесено в общее окно пере-снапшота именно потому, что платится один раз со всем батчем. +⚠ **Ключ `genre:` ОСТАВЛЕН ПРИНИМАЕМЫМ, и это не мягкость.** Платформа пишет книге `book.yaml` ОДИН раз из шаблона оператора и никогда не перезаписывает, протаскивая незнакомые ключи насквозь — `genre` там назван явно (`platform/deploy/README.md`). Декодер движка строгий (`KnownFields`), поэтому удаление поля отказало бы в загрузке книгам, которые уже лежат на диске, посреди живого прогона соседней сессии. Идиома — та же, что у ретированного `adult: false`. Значение не читается ничем: `TestARetiredGenreKeyStillLoadsAndReachesNothing` держит и загрузку, и равенство `BriefHash` у двух книг, отличающихся только жанром. +⚠ Лейблы промптов бампнуты и леджер пере-записан (`v1-reflow` → `v2-reflow-nogenre`, `v3-discourse-reflow` → `v4-discourse-reflow-nogenre`): байты промптов уехали, а лейбл обязан следовать за ними — иначе два прогона под одним лейблом несравнимы, что этот гейт и ловит. **Тем же касанием в свип леджера добавлен КЛАССИФИКАТОР:** он был вне его, пока ни один боевой конфиг его не включал, а его промпт решает КЛАСС термина, то есть маршрутизирует имя в транскрипцию, а реалию в перевод. +⚠ `genre: ранобэ` НАМЕРЕННО оставлен в `testdata/golden/book.yaml` с комментарием «не прибирать»: это место, где терпимость к ретированному ключу упражняется на каждом захвате. + +## Пункт 6 — дофикс чужого пака + ФЧ-7. ЗАКРЫТ + +⚠ **Это остаток ЧУЖОГО наряда (`D39.187` п.4), а не мой заказ** — сессия, делавшая тот пак, не пережила рестарт окружения. + +**(а) Развилка операторской строки прибита со ВТОРОЙ стороны.** Воспроизвёл сам: ДВЕ посадки в новый код `VolumeStop.String()` переживали всё дерево (проверено с `GOFLAGS=-buildvcs=false`, иначе `cmd/tmctl` падает на VCS-штампе копии и маскирует результат) — (1) развилка заголовка всегда истинна, (2) развилка заголовка спрошена про ПЕРЕНОС вместо ОСТАТКА. Обе печатают в смешанном состоянии «reached the END of the book … Still in the book: 1 unit(s) NEVER delivered». Прибито `TestTheStopLineSaysTheGrantHeldWorkBackWhenItDid` — таблицей из ДВУХ строк, потому что две потери теряются на разных состояниях: всегда-истинная падает на любой, а спрошенная-про-перенос только там, где перенос и остаток оба ненулевые. Записи каталога `WB1`, `WB2`. +**(б) Граница `bound()` прибита.** `Carried > 0` → `Carried > 1` переживало: существующий тест переносит ДВА юнита. Прогон с ОДНИМ перенесённым на конце книги не отдавал отчёт вовсе — тихий лимит, анти-паттерн `D39.165` §1б. `TestOneCarriedUnitIsStillWorkOutsideTheGrant`, запись `WB3`. +**(в) Три числа отчёта — ПРОВЕРЕНЫ И СХОДЯТСЯ на HEAD.** `D39.187` п.4(в) говорит «протухли снова»; пере-снял все три их собственными командами до своей работы: батарея `EXIT=0` с теми же тремя скипами · `^func Test` **1105** · каталог **92**. Отчёт 03.09 называет 1105 и 92. ⇒ пункт (в) закрыт лендингом `f8d3e9a`, и остатка по нему нет. + +**ФЧ-7 — ВОСПРОИЗВЕДЁН и ПОЧИНЕН. Починка СВЕРХ заказа, объявляю.** Промт заказывал проверку исполнением и починку только при воспроизведении. +Первая попытка воспроизведения ПРОВАЛИЛАСЬ и это важно: после потолка фидер `runWave` перестаёт раздавать элементы, поэтому второй воркер паниковать не успевает. Нужна синхронизация, чтобы обе позиции были в полёте, — и тогда воспроизводится ровно как описано: `runWave` отдавал ТОЛЬКО панику, `errors.As(err, &CeilingHalt)` → false, `errors.Is(err, errReserveCeiling)` → false, `terminal()` падал в `failed` и события `ceiling` НЕ писал. +Почему это дефект, а не размен: `platform/internal/runs/reconcile.go` читает событие потолка ПЕРВЫМ и дословно говорит «that survives ANY exit: the process may have been killed on its way out, and the fact does not become less true». `PD-113` закрыт ДВУМЯ независимо отказывающими каналами; одна паника забирала оба. +Починка узкая: `runWave` присоединяет к панике ТОЛЬКО `*CeilingHalt` — не `context.Canceled` (проверено посадкой: присоединение отмены роняет `TestWaveWorkerPanicSurfacesAsAFailingError`, и правильно, крах не должен читаться как вызванный им же graceful stop); `terminal()` проверяет панику ПЕРЕД потолком, пишет событие `ceiling` и исход `failed`. Код выхода 1 и зеркало «исход = код выхода» не двинуты. Пины: `TestACrashOverACaughtCeilingKeepsBothFacts`, `TestACrashOverACaughtCeilingStillAnnouncesTheCeiling`, `TestACrashJoinedWithACeilingStillExitsAsACrash`. + +**ПЕРЕ-СКОУП строки 197 — как заказано.** ФЧ-1/4/5 закрыты паком «число согласия» (`D39.187`). **ФЧ-6 сверен кодом и выглядит закрытым**: отказ проекции репортится базисом, отчёт не валится (`internal/pipeline/status.go`, греп `RebillBasisFailed`). **ФЧ-7 закрыт этим паком.** **ФЧ-2, ФЧ-3 и ФЧ-8 НЕ ПРОВЕРЕНЫ НИКЕМ и остаются живым остатком строки** — см. секцию «Что НЕ проверено». + +--- + +## Свобода и её разметка — отчёт ПО КАЖДОЙ строке таблицы промта + +**«Делай РОВНО так» — исполнено всё, отступлений нет:** порядок 113 → 46 соблюдён (база записана ДО стройки экрана) · СТОП по фикстуре не сработал, все три порога пройдены с запасом · текст заголовка не менялся · статусы строк и подписная таблица не тронуты · нота бита спеку у `FlagCJKArtifact` — флаг СВОЙ · суб-бюджеты ×4.47 как ПОЛ, взято выше с объявленным доводом · жанр снят и из промптов, и из канона брифа · обе посадки дофикса краснеют · ФЧ-7 проверен исполнением (и починен сверх заказа — объявлено) · все запреты «Границ» соблюдены. +**«Решаешь сам и аргументируешь» — по каждому пункту решение и довод выше:** развилка производства рода (§1) · второй замок — ВЫНЕСЕН пингом и ратифицирован оркестратором расщеплением, сам не снимал (§1) · данные в 134 — НЕ тронуты, объявлено первой строкой (§3) · числа суб-бюджетов (§4) · форма фикстуры 113 (§2, с названной ценой) · форма тестов и записей каталога · порядок работ (6 → 5 → 3 → 1 → 4 → 2, голден один раз в конце). +**Границы:** `manifestVersion`, `StreamVersion`, схема хранилища и версия контракта НЕ двинуты (предъявлено `tmctl manifest`) · новый эмбед-файл не заведён · `FlagCJKArtifact` не переиспользован · семантика переноса слота не тронута · строки 131 · 105(а) · 14 · 119 · 124 · 115 · 160 · 161 · 142 не брались · чужие зоны, `.env` и соседний worktree не трогались. + +## Самопроверка исполнением + +**1. Дифф `^func Test` ПОФАМИЛЬНО.** `grep -rn '^func Test' backend --include=*_test.go | wc -l` → **1105 → 1143**, добавлено 39. **Удалённых имён ОДНО:** `TestEditorSectionFollowsProvenance` — переименование, разбор в шапке. +**2. Голден пере-захвачен ОДИН раз, маскированный дифф разобран построчно.** 22 строки провода + 252 версионных. **Диспозиций, флагов и `chunk_status` не двинулось ни одного** (`diff` по этим строкам пуст; 252 строки до и после). +⚠ **Поправка к моей же формулировке, найденная адверсариальным проходом:** сказать «финальные тексты не изменились» было БЫ неверно. `final_text` изменился на всех 8 chunk-строках и `stage_text` на 22 — но ровно на ТОКЕН ХЕША, который фейк-провайдер эхом вкладывает в свой ответ, то есть следствием сдвига запроса, а не сменой вердикта. Точная формулировка: **вердикты не двинулись; каждый финальный текст отличается только эхом хеша запроса.** +⚠ **Вторая поправка того же прохода:** в ГОЛДЕНЕ двинулись `brief_hash`, `classifier_version`, `render_format_version` и `prompt_sha256` ЧЕРНОВОЙ стадии — а `memory_version` и `prompt_version` НЕ двинулись (у фикстуры свой лейбл `v-golden`, а токен фолда неподписанных строк заморожен намеренно). Мой первоначальный перечень осей был шире фактического. `memory_version` двигается на книге с гендерными строками, а не в этой фикстуре. +**3. Собственные посадки ВНЕ шести пунктов — 8, механизмом `cmd/tmmutate` на копии дерева.** 6 красных, **2 ПЕРЕЖИЛИ** и НЕ чинены (вне заказа, носитель — строка **252**): `StickyDepth = 2 → 1` не ловит никто · `priorityRank`, отбрасывающий приоритет доверия в бюджете инъекции, не ловит никто. Третья пережившая — `cutTag` без `lang.EmbeddedVersion()` — ПОЧИНЕНА по своей инициативе, потому что на этом компоненте стоит весь довод пункта 3 (`TestTheCutTagActuallyCarriesTheEngineData`, `TestEveryCutInputMovesTheTag`, запись `WB4`; `cutTag` разложен на `cutInputs`/`tag()` — рефактор байт-нейтрален, предъявлено сборками из HEAD и из дерева). +⚠ Методическая заметка, стоившая ложного результата: `tmmutate` на копии дерева НЕ МОЖЕТ судить тесты `cmd/tmctl`, которые собирают бинарь, — копия не git-репозиторий и сборка падает на VCS-штампе, маскируя всё остальное. Гонять копию с `GOFLAGS=-buildvcs=false`. +**4. Отдельный проход по СВОЕЙ ГОТОВОЙ работе свежим контекстом — ДВА агента, модель `fable`, оба read-only,** с заданием ФАЛЬСИФИЦИРОВАТЬ шесть заявлений каждый (деньги и провод · вердикты и данные). Ноль не декларирую: они принесли шесть находок, и четыре я починил. + +**Находки собственного адверсариального прохода — что нашли и что с этим стало:** +1. ⛔ **`bank-apply` СТИРАЛ произведённый род — то есть ровно тот акт, ради которого стоп существует.** `termFromBank` копировал только `Type` и `Aliases`; строка дельты уходила с `gender: ""`, а `loadAutoBank` потом ронял авто-строку как занятый ключ. ПОЧИНЕНО: род переносится вперёд, как переносятся алиасы (это находка движка ОБ СУЩНОСТИ, а решение о РЕНДЕРИНГЕ не просили её выбрасывать); дверь по-прежнему род не ПРИНИМАЕТ. Пин `TestApprovingATermKeepsTheGenderTheEngineProduced`, проверен красным на копии без правки. +2. ⛔ **Мой первый гард пункта 4 закрывал только одно из ДВУХ условий раннего возврата.** Второе — `r.pack == nil`, а нил-пак книга получает без `langpack_root` или с корнем без каталога своей пары. Такая книга несла бы настроенный и забюджетированный контур и не исполнила бы ни строки. ПОЧИНЕНО третьим гардом в раннере (пишущий путь), где гейт и пак есть вместе — конфиг-лоадер этого структурно не видит. +3. ⛔ **`repairReGate` звал `classify` БЕЗ `TargetScripts`** — экран цели на пути ремонта воздерживался, судя везде: починка, вернувшаяся на другом языке, прошла бы ре-гейт, вся работа которого в том, чтобы спросить «это всё ещё пригодный ответ». ПОЧИНЕНО: ре-гейт зовёт тот же классификатор с теми же входами. +4. ⛔ **`classify_budget_usd: 0.50` давал запас 1.45×, а комментарий рядом обещал «≈3×».** ПОЧИНЕНО: 1.00, запас 2.9×, и названо, что по замеру классификатор ДОРОЖЕ рендера. +5. ⛔ **Тест `TestTheLiveEchoGateKeepsItsMeasuredMargin` проходил на фикстуре с ПУСТЫМ off-target классом:** `0/0` = NaN, а любое сравнение с NaN ложно. ПОЧИНЕНО знаменателем-предусловием. Тем же касанием: суррогат целевой стороны привязан к записанному счёту (как уже был исходный), добавлены проверка `target_script_letters ≤ letters` и `TestTheScreenConstantsStillSitWhereTheCorpusPutThem` — константы `langscreen` сверяются с ДАННЫМИ, а не с константами (порог обязан лежать в пустом интервале корпуса, пол улики — не выше самого короткого off-target). +6. ⚠ **`allowshort_wire_test.go` утверждал ОТСУТСТВИЕ маркера — после пункта 3 это утверждение не может упасть.** Заменено на то, что фикстура ещё способна держать: `allow_short` доставляет строку ЦЕЛИКОМ, вместе с её родовой директивой. +Плюс поправлены четыре комментария, ставшие ложью: `decisions.go` («род — сид-онли без автопроизводителя»), доккоммент `langscreen` про →en, `BriefHash` про исторический хеш, и три стейл-упоминания маркера в тестах зоны. + +## Что НЕ удалось и что НЕ проверено + +⚠ Исходов ТРИ, и «не проверено» ≠ «опровергнуто» ≠ «сделано». + +**НЕ ПРОВЕРЕНО (никем, включая меня):** +- **ФЧ-2, ФЧ-3, ФЧ-8 строки 197.** Их я не открывал вовсе. ФЧ-2 — два докстринга `reprice.go` описывают не то, что делает код (детекция перебора односторонняя; «остаток несётся по счёту» неверно). ФЧ-3 — снятый закон approved-only продолжает утверждаться в докстринге теста и в `backend/docs/D15.2-*.md:317`. ФЧ-8 — докстринг `TranslateBook` про «каждый выход через `terminal()`» неверен для паники вне волны · комментарий `dispatchCommands` · шапка рига. Живой остаток строки 197. +- **Ничего в этом паке не проверено ПЛАТНЫМ вызовом.** Ни одна модель не вызывалась: санкции на деньги у меня не было и я её не просил. Всё измерено на персистированных чекпоинтах стенда и на фейк-провайдерах. +- **Качество рода не измерено.** Что классификатор реально отвечает в третьей колонке на живой книге — не мерено ни разу. Промпт написан консервативно (`none` объявлен самым частым и правильным ответом при нехватке контекстов), но верно ли он определяет пол — вопрос замера, а не стройки. ⚠ Это ПЕРВЫЙ кандидат в следующий полигон-пак: ось рода теперь производит данные, и ложный `male` поедет консистентно через всю книгу — то самое свойство, ради которого консистентность и строилась. +- **Экран цели не проверен на живом провале.** Он ловит два образца, которые уже лежат в стенде; поведёт ли он себя так же на СВЕЖЕМ off-target событии — не мерено. Порог 0.50 стоит в пустом интервале и не калиброван (и это по `D39.92` п.1а и не может быть иначе на этом корпусе). +- **Цена банкового контура в c1 на реальной книге не мерена.** Числа суб-бюджетов — проекция от десятиглавого замера, не наблюдение. Первый же боевой прогон с контуром даст настоящие числа, и их надо сверить с полом. +- **Наличие корпуса контраста на БОЕВОЙ машине.** Я его не видел и видеть не мог. Строка 251. + +**НЕ СДЕЛАНО СОЗНАТЕЛЬНО (с доводом):** +- **Замок №0 не трогал** — базовый банк исключает `Source:"mined"` по денежному контракту `D39.42` п.3. Следствие названо в §1: род не едет на ЧЕРНОВОЙ провод и не поедет, пока цена двух волн не решена отдельно. Не моё решение. +- **`ru glossary_header` не тронут** — висячая ссылка на снятый маркер, чинится только правкой `injection.txt`, то есть ценой `cutTag`. Носитель — строка 250. +- **Две пережившие мутации не чинены** — вне заказа, носитель 252. +- **Строка 204 / `PD-246`: карту причин платформы не правил** — чужая зона. Пинг отправлен напрямую (`textmachine-main-34`), она пере-сняла счёт своей командой и завела у себя; состав контрактного минора несёт оркестратору, потому что изобретать код замечания платформенная сессия не вправе. + +**НАЙДЕНО И НЕ ЧИНЕНО — ЧУЖИЕ ЗОНЫ (передаю фактом, не правкой):** +- ⚠ **Следствие починки ФЧ-7 на стороне платформы, которого я не предвидел.** `pgstore/sink.go` кладёт событие `ceiling` в `paused_reason`, а `runs/reconcile.go:1008` возвращает `paused` с `exit_code = nil`. ⇒ прогон, который УПАЛ поверх потолка, теперь приезжает `paused` и КОД ВЫХОДА теряется — крах становится невидим на строке прогона. Это лучше прежнего для пользователя (ему говорят «добавь денег», и это правда и действие), но хуже для операций. Взвесить и решить — не мне; несу как факт. +- ⚠ **Стейл-упоминания снятого маркера вне моей зоны:** `platform/internal/runs/spawn.go:164` (прод-комментарий), `platform/internal/runs/reconcile_test.go:723`, `platform/docs/DEFECT_REGISTER.md:412` (PD-277), `eval/pilot/memory_eval.py:440-452` (заявляет «byte-synced with Go glossaryBlockHeader» и по-прежнему дописывает маркер построчно — зеркало провода в полигоне разъехалось). +- ⚠ **`platform/deploy/` ничего не знает о корпусе контраста** (`grep -rn 'contrast_path\|mining:' platform/deploy/` → пусто). Половина строки 251. +- ⚠ **Дыра покрытия, найденная платной сессией и переданная мне** (`PD-441`, её ряд): вызов, отменённый В ПОЛЁТЕ по книжному потолку, уходит веткой «No 2xx ever arrived: nothing was billed» → `releaseReservation` и нулевая цена в `request_log`, хотя замер 04.09 показывает отмены на 120756/156469/156470 мс — то есть запрос был выпущен и, скорее всего, оплачен. Латентность разводит два случая (20–31 мс против 156 с). **Я это НЕ чинил: `stagerun.go` в моей зоне, но это ДЕНЕЖНЫЙ путь и отдельный предмет, а не пункт вайр-батча.** Заказ на него — отдельным паком. + +**ЧЕГО Я НЕ ДЕЛАЛ ПО ГРАНИЦАМ:** не коммитил, индекс и историю не трогал; `platform/` и `eval/` только читал; соседний worktree не открывал; `.env` не читал. + +## ДОФИКС ПО ПРИЁМКЕ (04.09) — четыре пункта наряда, все закрыты + +⚠ **Класс, который приёмка вскрыла и которого моя самопроверка не нашла: СЕМЬ ЗАЯВЛЕННЫХ ГАРАНТИЙ СУЩЕСТВОВАЛИ ТОЛЬКО КАК КОД.** Двенадцать её посадок пережили полную батарею. Это ровно образец `D39.187` — тот же дефект, что этот пак чинил у чужого пака, повторённый мной. И нашёлся он там, где я сам сказал «ставь гуще», то есть карта слепых зон работает; чего у меня не было — это привычки сажать мутации на СВОЙ новый код, а не только на соседний. + +**Д1 — пять неприбитых гарантий прибиты, каждая тестом И записью каталога (`WB5`–`WB13`, каталог 96 → 105, все красные одним прогоном).** +· **F2, тяжелейшая: живая доставка строки 46 не была прибита вовсе** — `grep -rn 'FlagOffTargetLang' --include=*_test.go` давал ПУСТО. Модуль экрана был прибит офлайн (по руносчёту фикстуры) и по своему пакету, а вердикт, ради которого он построен, на живом пути не проверялся ни разу: переживали и «экран всегда воздерживается» (`Screen(out, nil)`), и «`chunkrun` не передаёт `TargetScripts`», и **снятие флага из `escalatable()` — прямое требование `D39.93` п.2, без которого пойманный чанк молча ТЕРЯЕТСЯ вместо пере-атаки**. Прибито: `TestAnOffTargetCompletionIsFlaggedAndEscalated` (вердикт + деталь + `escalatable` + `!retryable` + эхо остаётся эхом), `TestTheScreenIsInertWithoutTargetData`, `TestTheRunnerHandsTheScreenItsData` (через `classifyOutput` — ЕДИНСТВЕННЫЙ живой call-site, плюс исключение обеих банк-ролей). +· **F10: порог экрана не был ИСПОЛЬЗОВАН ни в одном тесте** — случая с долей между границами не было, то есть константа могла быть любой. `TestTheOffTargetThresholdIsUsed`: 0.60 → ok, 0.40 → флаг. +· **F1: три гарда контура** — `TestABankContourThatCanNeverFireIsRefused` (лоадер судит КЛЮЧ и не судит ФАЙЛ) и `TestTheContourRefusesAWriteRunItCannotExecute` (обе половины раннего возврата: отсутствующий артефакт и нил-пак; с ПРЕДПОСЫЛКОЙ, что до порчи фикстура открывается, и с обязательной проверкой, что `$0`-чтение той же книги ПО-ПРЕЖНЕМУ работает — `D20.4`). +· **F5: гард `TargetScriptNonLatin` в санитайзере** — `TestTheLatinResidueClassIsGatedOnTheTargetsOwnScript`, обе стороны. ⚠ Названная узость: `target-en.txt` в репо нет, поэтому «латинописьменная цель» достижима только через ПУСТОЕ объявление (та же ветка `wordScript == nil`); строку с настоящим `word_script latin` тест получит в день, когда такая цель появится. +· **F6: исключение банк-ролей** прибито было только юнитом на `isBankRole`; регресс до «терминолог-only» оставлял батарею зелёной. Теперь ловится через `classifyOutput` (`WB7`). + +**Д2 — $0-валидация плейсхолдеров ДО первого вызова. ДЕНЬГИ.** +Приёмка подтвердила исполнением то, что я принёс и не починил, сочтя вопрос про громкость: отказ на `{{genre}}` приходил ПОСЛЕ купленной черновой волны (`stage=draft cost_usd=0.001820`, `edit wave started`, затем отказ; `drafts=1 edits=0`). **Вопрос был про МОМЕНТ, и я его не расслышал.** Построено: `CheckPlaceholders` сканирует шаблон по тому же ЗАКРЫТОМУ набору, что подставляет `Render` — набор вынесен в одно определение (`renderVals`), потому что два списка это и есть способ, которым валидатор благословляет имя, которое рендер отвергнет. Вызов стоит в `LoadPromptTemplate` — единственной воронке, через которую проходят ВСЕ шаблоны: стадии, классы репэйра и обе банк-роли. Живой источник таких пакетов настоящий: `grep -rl '{{genre}}' eval/ | wc -l` → **27**. +⚠ **Заказанная смена поведения, объявляю (D39.183):** `TestRenderRejectsUnknownPlaceholder` описывал отказ ОТ `Render`; отказ переехал на загрузку. Тест не ослаблен — он теперь держит ОБА рубежа (гейт на загрузке И собственный отказ `Render`), плюс что закрытый набор проходит целиком, плюс что отказ НАЗЫВАЕТ и маркер, и то, что движок принимает. + +**Д3 — закон-блок даёт ОДИН приказ на исходный термин. Ратификация исполнена ровно так.** +Дедуп переведён с `(src,dst)` на `src`. Довод, который я принимаю целиком и не смягчаю: пока у блока было два заголовка, две передачи одного термина были двумя РАЗНЫМИ высказываниями — «это канон» и «это неподтверждённый рабочий вариант»; **мой же пункт 3 схлопнул их в два ПРИКАЗА об одном термине**, а с разными родовыми директивами — в два противоречивых приказа об одном персонаже. Побеждает первая строка, и «первая» тут не случайность: `Select` отдаёт в приоритетном порядке, `priorityRank` ставит `Confirmed` раньше `Ambiguous` — то есть подписанная. Конфликт не спрятан, а РАЗВЕДЁН: движок уже пишет о нём WARN оператору (`seeding.go`), и это правильное деление — **модель получает один закон, оператор получает разногласие**. Отбрасывание строки на загрузке не бралось: это онтология банка, а нужна была узкая правка провода. Пин `TestTheLawBlockGivesOneOrderPerSourceTerm` кормит неподписанную строку ПЕРВОЙ (дедуп, хранящий первую строку ВХОДА вместо первой строки приоритета, прошёл бы фикстуру в естественном порядке) и отдельно держит, что ГОМОНИМИЧНЫЙ dst — два разных исходных термина — остаётся двумя строками. + +**Д4 — числа отчёта поправлены.** F7: «ноль ложных на 1132» заменено на воспроизводимое над репозиторием — **ноль ложных на 44 ТРУДНЫХ здоровых**, с явным разделением «обход дал 1162/1132» и «в git уехала выборка 74 строк», и с описанием, как выборка отобрана. Это ровно класс «число одной величины под именем другой», который я чинил в пункте 4 и повторил в своём же отчёте. F8: расхождение шапки и секции самопроверки (1133/29 против 1135/31) снято — обе теперь **1143**, добавлено 39. + +⚠ **F11 — РИСК ДИЗАЙНА, не чинен, называю (вход в калибровку).** На латино-тяжёлом ЗДОРОВОМ русском экран цели даёт ложные `off_target`: русский с пиньинь-глоссами → 0.461, инструкции с shell-командами → 0.077. Ложный флаг ЭСКАЛИРУЕТСЯ, то есть стоит платного вызова. Корпус, на котором порог поставлен, — ОДНА сянься-новелла; жанр с системными окнами, кодом или плотной латинской терминологией не мерен вовсе. **Перед первым платным прогоном книги другого жанра порог обязан быть пере-калиброван на её корпусе.** Это не опровержение экрана (на измеренном жанре ноль ложных на трудных), это граница его калибровки. + +**Батарея после дофикса:** `MAKE_EXIT=0`, `0 issues`, 19 пакетов `ok`, скипы те же три. **Голден пере-захвачен — 0 изменений** (дофикс не двинул ни одного байта провода в фикстуре). Тесты **1105 → 1143**, удалённых имён по-прежнему одно и то же переименование. Каталог **92 → 105**, записи `WB1`–`WB13` красные. + +## КРУГ 2 ПЕТЛИ (`D39.191`) — НЕ ЗАМКНУЛСЯ: веер нашёл 21 пережившую посадку, все закрыты. Круг 3 обязателен + +⚠ **Условие замыкания жёсткое, и оно не выполнено:** круг замыкается, только когда проход с НЕПУСТЫМ веером не нашёл ни одной пережившей посадки. Этот нашёл двадцать одну. Ниже — веер, все находки с диспозициями, что я перепроверила сама, и чего круг не покрыл. + +### Веер +**Три источника, разные ракурсы.** (1) Агент `fable`, read-only — ДОФИКС, которого свежий контекст не видел: `CheckPlaceholders`, дедуп по `src`, новые тесты гардов и экрана на ВАКУУМНОСТЬ. (2) Агент `fable`, read-only — НЕПОКРЫТОЕ, ровно по моему же списку слепых зон: `volume.go` (пункт 6, чужой дофикс), закон-блок на книге с НЕПОДПИСАННЫМИ строками, бласт-радиус `InjectionTrust`, свип леджера, мелкие поверхности. (3) **МОИ собственные посадки — В ДАННЫЕ**, чего в круге 1 не было вовсе: фикстура корпуса, `prompt-labels.json`, `pipeline-c1.yaml`, промпт классификатора, `injection.txt`. +**Посадок всего: 12 моих + ~26 агента A + ~60 агента B.** Мои: 11 красных, 1 пережила. + +### Что нашлось — двадцать одна пережившая, по классам + +**А. ЖИВОЙ ДЕФЕКТ, внесённый моим же дофиксом (1).** +· **Дедуп закон-блока по голому `src` ронял ВТОРУЮ ПОДПИСАННУЮ строку.** Ключ уникальности банка — `(src, sense, since, until)`, то есть одна поверхность законно несёт несколько подписанных канонов: 青山-фамилия и 青山-направление. Мой дедуп выбрасывал один из них МОЛЧА, при том что трансляторный блок продолжал рендерить оба — два провода разъезжались. Правило исправлено на то, чем оно должно было быть с самого начала: **уступает НЕПОДПИСАННАЯ строка, и только подписанной; подпись не роняется никогда.** Пин красный на возврате к старому ключу. + +**Б. ДЕНЬГИ (2).** +· **`ReachableModels` не знал банк-ролей.** `buildClients` пре-строит именно этот список, `CheckKeys` требует именно эти ключи — оба на ОТКРЫТИИ. Модель терминолога или классификатора, не заведённая в `models.yaml` или без ключа в окружении, проходила ВЕСЬ преflight и убивала прогон на стопе майнинга — после купленной черновой волны. Латентно, пока контур не жил в боевом конфиге; пункт 4 делает это живым. +· **Банкнотный канал покупает вывод, который на этой конфигурации никто не прочитает** — единственный читатель за ранним возвратом. Не отказ (прогон корректен, срез делает свою работу), а предупреждение с ценой. + +**В. УТВЕРЖДЕНИЯ О СТРУКТУРЕ, ВЫДАННЫЕ ЗА ГАРАНТИИ (4) — класс, которого я в круге 1 не назвала.** +· «валидатор и рендер читают одну таблицу» — прибито только ПРИМЕРАМИ; валидатор-надмножество проходил всё; +· «проверка стоит в единственной воронке и покрывает все пути» — **все четыре пути загрузки промптов обводятся вокруг гейта, батарея зелёная.** Моя же формулировка из отчёта по Д2; +· «порог лежит в пустом интервале» — порог 0.50 → 0.45 переживает всё И НЕ ДВИГАЕТ ВЕРСИЮ: резюмнутый чанк пере-судился бы по порогу, за который никто не платил. ⇒ `langscreen.Version` теперь ВЫВОДИТСЯ из констант, `classifierVersion` стал var, правка порога роняет голден по построению; +· «свип леджера покрывает обе банк-роли» — свип ходил по НАЙДЕННОМУ и никогда по ЗАПИСАННОМУ, поэтому удаление роли из свипа оставляло её запись сиротой, а промпт — неприбитым. ⇒ проверка сирот. + +**Г. ДАННЫЕ (1) — и это то, ради чего норма про посадки в данные написана.** +· **Фикстура корпуса, размягчённая до красивого числа, проходила всё.** Тест утверждает пол «не меньше 3×», а пол не измерение: обнуление исходных рун у ЕДИНСТВЕННОГО худшего здорового образца оставляло батарею зелёной и рапортовало запас ЛУЧШЕ, чем корпус когда-либо давал. ⇒ леджер записанных чисел (30/44 строк · 0.011655 · 0.976706 · 346 букв), по образцу леджера лейблов: пере-взять корпус законно, но это стоит правки здесь и фразы в отчёте, а не ничего. + +**Д. РАСЩЕПЛЕНИЕ `Disp`, прибитое только там, где две половины СОВПАДАЮТ (3).** +· липкий перенос, ПЕРЕ-ВЫЧИСЛЯЮЩИЙ матч-доверие, переживал всё: единственный пин использует строку, чей `src` И ЕСТЬ сработавший ключ, поэтому пере-вычисление совпадает с наследованием. Разводит их строка с ханьским `src` и коротким фонетическим АЛИАСОМ — там перенос re-trusted, и род едет к сущности, которой на странице может не быть; +· липкий перенос, ВЫВОДЯЩИЙ доверие из диспозиции, тоже переживал: неподписанная строка на чистом ключе теряет род в момент переноса — дверь строки 210 в липкой форме; +· диспозиционная ось `priorityRank` не отличима от статусной ни одной фикстурой. Это одна из ДВУХ, переживших круг 1, и теперь на ней стоит мой дедуп. ⇒ прибита состоянием, которое оси разводит: две ПОДПИСАННЫЕ строки, одна на collision-prone ключе, под связывающим бюджетом. + +**Е. ЧУЖОЙ ПАК (пункт 6), которого свежими глазами не видел никто (6).** Из двадцати посадок в `volume.go` шесть пережили, и все шесть — в ОПЕРАТОРСКОМ ПРЕДЛОЖЕНИИ и в двух неотфикстуренных ветках `reconcile`: счёт «%d paying output unit(s)» без флагнутых (недо-счёт того, за что заплачено) · клауза про $0-попутчиков · развилка головы, спрошенная про `LeftFresh` вместо `Left()` · свободный юнит, флагнувшийся на резюме, посчитанный оплаченным · пере-деланный юнит, флагнувшийся и оставшийся В ОБОИХ счётчиках · и **порядок `bound()` ДО `reconcile`, от которого корректность отчёта зависит СЛУЧАЙНО**. Прибиты пятью тестами; последний — документирован в `waverun.go` с прямым указанием, какой «естественный рефактор» его сломает. + +**Ж. Прочее (4):** липкие записи, исчезающие из закон-блока · глубина липкого окна · сироты леджера · `injection.txt` без родовой строки (последнее — уже красное, поймано `TestDC3GenderInjection`). + +### Что я перепроверила сама, а что взяла со слов +**Перепроверила посадкой ДО починки (все, кроме одной):** дедуп по `src` · порог экрана · валидатор-надмножество · обход воронки на пути классификатора · диспозиционная ось · липкий перенос (обе формы) · сирота леджера (обе формы) · липкая запись в закон-блоке · банкнотный канал · размягчённая фикстура. +**Взяла со слов:** `H2` (два ряда с одним id — недостижимо через стор), `H7` (битый артефакт контраста молча даёт пустой контраст), и стейл-текст `glossary_header` — про него я уже знала. +⚠ **И одна собственная ошибка, которую поймал каталог, а не я:** первая редакция теста про `ReachableModels` читала `pipeline-c1.yaml`, где модель банк-роли СОВПАДАЕТ с моделью черновика, — то есть тест не мог упасть. Посадка сказала `SURVIVED`, я переписала тест на конфиг, где банк-модель своя. **Механизм проаудировал мой пин; чтение бы не проаудировало.** + +### Чего круг НЕ покрыл +- **Пункт 5 (жанр) и цепь рода end-to-end** — ни один агент их не атаковал: я дала им дофикс и непокрытое, считая эти два закрытыми кругом 1. Остаются проверенными одним проходом. +- **`$0`-проекции на дефектной книге** — `status`/`report`/`export`, реально отрендеренные: и мой тест, и посадки доказывают только, что КОНСТРУКТОР открывается. +- **Липкий перенос до РЕДАКТОРСКОГО провода сквозным прогоном** — ни одна фикстура не гонит прономинальный чанк с переносом в edit-юнит; всё прибито на уровне `membank`. +- **Настоящая латинописьменная цель** — `target-en.txt` в репо нет; ветка достигается пустым объявлением, и тест это говорит. +- **`eval/`-харнессы** — вне модуля бэкенда, не гонялись. +- **Сверка двух агентов между собой** — снова не делалась. + +### Числа круга +Батарея `MAKE_EXIT=0`, `0 issues`, 19 пакетов, скипы те же три. **Голден БАЙТ-ИДЕНТИЧЕН** тому, что был до починок круга (единственное движение голдена за круг — `classifier_version` от вывода версии экрана из констант; вердикты не двинулись). Тесты **1105 → 1156**. Каталог **92 → 120**, записи `WB1`–`WB29` красные одним прогоном. Удалённых имён по-прежнему одно — переименование `TestEditorSectionFollowsProvenance`. + +## КРУГ 3 ПЕТЛИ (`D39.191`) — ЗАМКНУЛСЯ ПО СОДЕРЖАНИЮ, НО НЕ ПО УСЛОВИЮ: веер нашёл 20 переживших посадок и 1 дыру исполнением, все закрыты + +⚠ **Условие замыкания не выполнено и здесь.** Круг замыкается, только когда проход с непустым веером не нашёл НИ ОДНОЙ пережившей посадки. Этот нашёл двадцать — плюс дыру, найденную не посадкой, а исполнением. Ниже веер, все находки с диспозициями, что я перепроверила сама, и чего круг не покрыл. + +### Веер +**Два источника, и это первый круг, где второй — не мой агент.** (1) Агент `fable`, read-only, работавший в СВОИХ копиях дерева (рабочее дерево не тронуто — подтверждено им и сверено по `git status`): **68 посадок в семи предметах**, 48 красных, 20 переживших, 0 несостоявшихся. (2) **Круг оркестратора** — независимый веер по тому же батчу, девять находок, пять новых для меня. По трём пунктам два веера сошлись НЕЗАВИСИМО (дедуп полисемии · порог без пина · класс структурных утверждений), и это первое за три круга свидетельство, что находки воспроизводимы между читателями, а не являются вкусом одного. + +### ⛔ ГЛАВНОЕ: денежная дыра, которую круг 2 ОБЪЯВИЛ ЗАКРЫТОЙ, была открыта + +Мой фикс круга 2 внёс банк-роли в `ReachableModels()` и на этом остановился. **`CheckKeys` читает этот список ТОЛЬКО в лейбл-ветке.** Безлейбловая книга — shipping-дефолт — идёт второй веткой, которая собирала модели стадий руками и про ворота не знала. Проверено исполнением: терминолог, классификатор и ремонт на провайдере с пустым `api_key_env` проходили префлайт целиком, `CheckKeys` возвращал nil. Прогон покупал черновую волну и умирал на воротах. + +**Почему тест круга 2 был зелёным поверх дыры:** он утверждал СПИСОК, а не префлайт. Пин на списке не есть пин на проверке, которая его читает. + +**И вторая половина той же дыры:** рантайм-список `reachableModels()` тоже не был прибит — посадка «только стадии» (без ремонта и банк-ролей) держала ВЕСЬ пакет `pipeline` зелёным. Причина — совпадение `pipeline-c1`, воспроизведённое в фикстурах: каждая фикстура ставит воротам `model: fake-model`, ту же модель, что у стадий. **Закрыто:** одно определение `gateModels()`, которое читают обе ветки `CheckKeys` и `ReachableModels`; тест зовёт `CheckKeys`, а не смотрит список; и фикстура, где банк-роль ходит СОБСТВЕННОЙ моделью, с пином на пре-строенной карте клиентов. + +### Что нашлось ещё — по классам + +**А. ЦЕЛЬ С CJK-ПИСЬМОМ НЕ ЭКРАНИРОВАЛАСЬ ВООБЩЕ** (находка оркестратора). Гейт `!isCJKTarget` принадлежал ЭХО-правилу, а держал оба правила сразу. «Вывод в письме ИСТОЧНИКА» для →ja бессмысленно — верный перевод zh-книги полон ханьцзы; «вывод в письме ЦЕЛИ» отвечается прекрасно. Итог: →ja/→zh/→ko не судились ничем, при том что доккоммент `langscreen` обещал обратное. **Это бьёт в цель проекта №2:** на ревью-вопрос «заработает ли пара, которой в репо нет, без правки Go» ответ для CJK-целей был «нет». Экран вынесен из-под гейта, эхо-правило осталось под ним. Документированный предел супермножества (китайский вывод для →ja не флагуется) прибит **как ПРОМАХ**, чтобы никто не принял экран за защиту от zh-эха на →ja. + +⚠ **`classifierVersion` НЕ двигали — ратифицировано оркестратором.** Для →ru поток управления побайтово тот же (`!isCJKTarget` истинно и до, и после, обе ветки в том же порядке), →CJK-снапшотов не существует, а строка в этом батче уже сдвинулась дважды и пере-снапшот уже предписан единым актом. Различитель на будущее: **бамп должен любой смене, способной пере-вердиктить СУЩЕСТВУЮЩИЙ снапшот** — не «кажется, безопасно». + +**Б. ПРОИЗВОДНОСТЬ ВЕРСИИ ЭКРАНА БЫЛА ЗАЯВЛЕНА И НЕ ДЕРЖАЛАСЬ (3 посадки).** Доккоммент `Version` говорит «выведена по построению, поэтому сдвиг порога забирает громкий `--resnapshot` с собой». Не проверял никто. Посадка: переписать `var` литералом, который он сейчас печатает, и сдвинуть порог 0.50 → 0.45 — строка не двинулась, голден не двинулся, оба интервальных пина прошли (0.45 внутри пустой полосы), весь пакет зелёный. Резюмированный чанк пере-вердиктился бы порогом, за который никто не платил. Третья дверь: `%.2f` не выражает 0.504 — окно точности ±0.005. **Закрыто:** тест пересчитывает строку из констант, и round-trip-guard требует, чтобы напечатанный порог читался обратно тем же числом. Формат НЕ расширен намеренно — это стоило бы `--resnapshot` всем книгам за правило, которое не менялось; расширение теперь осознанный акт. + +**В. ГРАНИЦА СРАВНЕНИЯ, КОТОРУЮ НЕ ВЫРАЖАЕТ НИ ОДНА КОНСТАНТА.** `share < floor` → `<=` пережило весь пакет: ни одна фикстура не сидела ровно на линии. Прибито фикстурой ровно на 200 буквах, из которых ровно 100 целевых — доля 0.50 без плавающего люфта. + +**Г. ВАЛИДАТОР ПЛЕЙСХОЛДЕРОВ — ТРИ ДВЕРИ.** (1) Сканирование только System-части пережило всё — каждая фикстура кладёт плохой маркер в системную половину, а самое вероятное место для автора — пользовательский хвост, где живут почанковые переменные. (2) Снятие проверки незакрытого `{{` пережило. (3) Тест «равенства множеств» обходит `placeholderNames()`, то есть таблицу РЕНДЕРЕРА, и доказывает только «рендерер ⊆ валидатор»; дорогое направление — обратное, и оно было прибито четырьмя ЛИТЕРАЛЬНЫМИ словами, так что пятое слово проходило насквозь. **Закрыто устранением, а не пином: валидатор ТЕПЕРЬ ЕСТЬ РЕНДЕРЕР** — `CheckPlaceholders` рендерит по настоящей таблице значений. Второго списка не существует, расходиться нечему. + +**Д. ЗАКОН-БЛОК: ПОДПИСЬ ПРОТИВ УВЕРЕННОСТИ (5 посадок).** Подпись — это СТАТУС, а не уверенность матча: подписанная строка, сработавшая по сомнительному ключу, всё равно канон, которому уступают. Ключевание набора подписей на диспозиции пережило. Уступать должен ЛЮБОЙ неподписанный статус, а не только `draft` — `auto` пишет майнер, то есть ровно та строка, ради подчинения которой правило и существует. Плюс: подписанная строка с ПУСТЫМ переводом не канон (иначе термин исчезает из блока), несимметричный `TrimSpace` разрушает набор, и однопроходная версия зависит от порядка. **Отдельно — вакуумность в моём собственном тесте:** его комментарий утверждал, что случай (1) держит независимость от порядка, а стоящий там же `sortByPriority` это утверждение обнулял. Комментарий исправлен, независимость от порядка вынесена в отдельный случай, который рендерит блок НАПРЯМУЮ, в обоих порядках. + +**Е. ЛИПКИЙ ПЕРЕНОС: ПОЛОВИНА ДИСПОЗИЦИИ.** Перенос наследует обе половины, но пересчёт диспозиции ИЗ СОБСТВЕННОЙ ПОВЕРХНОСТИ строки пережил: в прибитой фикстуре `src` сам и есть сомнительный ключ, поэтому пересчёт воспроизводит тот же ответ. Разделяющая фикстура (ханьский `src`, сомнительный кана-алиас) существовала, но утверждала только `KeyTrusted`. Под посадкой перенос молча повышался до `Confirmed` — пропуск принудительного пост-чека и место повыше в бюджете, — а провод молчал, потому что половина доверия цела. Это D16.2-повышение в чужом пальто. + +**Ж. ЛЕДЖЕР КОРПУСА ПИНИЛ ЭКСТРЕМУМ, А НЕ ПОПУЛЯЦИЮ (5 посадок).** Второй по трудности здоровый образец можно было смягчить, второй по низости целевой — тоже, а трудную строку — заменить числами лёгкой ПОД ЕЁ ЖЕ id: счётчики, экстремумы и множество id держались. И у интервала не было записи про ВТОРУЮ сторону: доля off-target могла уехать 0.000 → 0.490 при всех зелёных тестах, и порог 0.50 сел бы прямо на образец, пока леджер говорит «полоса пуста». **Закрыто:** запись `wantBestOffTargetTgt` и **дайджест по всей популяции** (id · метка · четыре счётчика, сортировка по id) — пере-взятие корпуса стало осознанным актом, переписывающим строку в том же коммите. + +**З. ОПЕРАТОРСКОЕ ПРЕДЛОЖЕНИЕ: ЧИСЛО ФЛАГОВ ПЕЧАТАЛОСЬ ЧИСЛОМ ПЕРЕДЕЛОК.** Пережило весь пакет: обе фикстуры, читающие это предложение, имели `Reworked == Flagged == 1`. Разведены. + +**И. ФЛАГ, РАДИ КОТОРОГО ПОСТРОЕН ПУНКТ 2, БЫЛ САМЫМ БЕЗОБИДНЫМ В ПАСПОРТЕ** (находка оркестратора). `flagReasonSeverity` не знал `off_target_lang` → дефолт 8, ниже `length` (6). Глава, вернувшаяся не на том языке, показывала «length» как худшую проблему. У функции не было ни одного теста. **Закрыто:** switch стал картой, а исчерпаемость держит тест, читающий константы `FlagReason` **из исходника через go/ast** — список в тесте гнил бы ровно так же, как список в коде, и заверял бы гниль. + +**К. ВТОРОЙ CALL-SITE `classify`** (находка оркестратора). `repairReGate` собирал `classifyInput` руками из тех же полей, что `classifyOutput`. Поле `TargetScripts` однажды уже забыли там, починили руками — и посадка показала, что починку не держит ничто. **Закрыто устранением:** один конструктор `classifyInputFor`, пин на нём через рефлексию — любое поле, пришедшее нулевым, валит тест. Список полей в тесте не пишется: его обновлял бы тот же, кто забыл обновить конструктор. + +**Л. ДВЕРЬ 3 ГЕНДЕРНОГО ТЕСТА ОБЕЩАЛА НЕ ТО, ЧТО ДЕРЖИТ** (находка оркестратора, подтверждена исполнением). Её комментарий говорил: «впустят mined-строки в базовый банк — покраснею». Не краснеет: внутри ОДНОГО прогона черновая волна покупается ДО ответа классификатора, поэтому никакое правило про банк не положит род на тот провод. Утверждение об отсутствии оставлено как утверждение о ПОРЯДКЕ, а денежный контракт теперь утверждается там, где живёт — на двух материализованных банках: обогащённый несёт mined-строку, базовый её не выбирает. + +**М. ИНСТРУМЕНТ ТЕРЯЛ СВОЙСТВО «ГОНЯЕТСЯ ЦЕЛИКОМ»** (находка оркестратора). Протухший якорь `AF-keys-file-reason-covers-all` (протух ещё до этого пака, на `8adcb86`) возвращал ОШИБКУ ПРОГОНА и клал весь каталог на 33-й записи из 120 — а прогоны через `-id` продолжали проходить, и никто не видел. **Закрыто:** протухание стало ПЕРЗАПИСНЫМ вердиктом `ROTTED` — прогон идёт дальше, выход остаётся ненулевым, запись предъявляется. Якорь починен. + +### Что я перепроверила САМА, не приняв на слово +- **Три находки оркестратора, где код на вид противоречил заголовку** (`repair.go` передаёт `TargetScripts`; `CheckPlaceholders` сканирует весь `canon` и ловит незакрытый `{{`; `TestUnionStickyNewestWins` на вид ловит keep-first) — прогнала собственные посадки. Две подтвердились как непокрытость, одна — нет. +- **`UnionSticky` (его находка №4) — ОТВЕДЕНА.** Четыре независимые посадки красные. Оркестратор проверил свою на чистой копии `b8154cd` и снял её сам с точным разбором: его «инверсия» (обход новое→старое с keep-first) ПОВЕДЕНЧЕСКИ ЭКВИВАЛЕНТНА оригиналу (обход старое→новое с перезаписью). **Третий класс ложной зелени за сутки — посадка, не меняющая поведения**, рядом с моими двумя: упавшая сборка, прочитанная как зелень, и обрезанный вывод, прочитанный как ноль. Общее у всех трёх: **читатель верит ВЕРДИКТУ, не проверив, что его произвело.** +- **Дыра `CheckKeys`** — не поверила отчёту агента: написала тест, увидела его красным на всех трёх воротах, потом чинила. +- **Каждая из 19 новых записей каталога предъявлена красной прогоном.** Одна (`WB41`) вернулась SURVIVED — **моя фикстура случайно давала верный ответ и под посадкой** (неподписанная строка была `Confirmed` и сама наполняла набор подписей). Фикстура переписана так, что подпись — единственный возможный источник; предъявлена красной. + +### ⚠ Чего круг НЕ покрыл +- **Причина `S6e`** — совпадение ли это фикстур или у банк-ролей есть второй путь к клиенту помимо `clientFor`. Названо агентом, не открыто; я закрыла СЛЕДСТВИЕ (пин на карте клиентов), не причину. +- **Сквозной прогон дыры `CheckKeys`** через `NewRunner` → волна: закрыто на уровне `CheckKeys` и пре-строенной карты, но что происходит на живом проводе с ключом-пустышкой, никто не гонял. +- **Резюме через смену версии** — end-to-end пере-вердикта резюмированного чанка нет ни одного теста; вся аргументация про `--resnapshot` держится на чтении. +- **Конвенционный путь стадийного промпта** — пер-путевой тест гоняет только `prompt_override`. Маркер внутри вырезаемого комментария проходит по построению, и НАМЕРЕННОСТЬ этого никем не проверена. +- **Два НЕПОДПИСАННЫХ ряда одной поверхности** оба рендерятся: «один приказ на термин» держится только когда подпись есть. Наблюдение агента, теста нет ни в ту, ни в другую сторону. +- **`precomputeSticky`** — сброс на границе главы и запись переноса, заблокированного спойлером. +- **Сам харнесс `tmmutate`** (отпечаток и восстановление) и единственная запись `expect: survives` (`G-byte-gate`) не ревьюились ни одним кругом из трёх. +- **Ракурсы, унаследованные непокрытыми из круга 2 и не открытые снова:** `$0`-проекции на дефектной книге · липкий перенос до редакторского провода сквозным · настоящая латинописьменная цель (`target-en.txt` в репо нет) · `eval/`-харнессы вне модуля. +- **F11 остаётся риском дизайна, не чинен:** порог экрана калиброван на ОДНОЙ сянься-новелле; на латино-тяжёлом здоровом русском даёт ложный `off_target` (0.461 с пиньинь-глоссами, 0.077 с shell-командами), а ложный флаг ЭСКАЛИРУЕТ — то есть стоит платного вызова. Оркестратор забрал носителем. +- **Круг 3 не ревьюил мои правки круга 3.** Копии агента сняты до них. Четвёртый круг, если он будет, начинается отсюда. + +### Числа круга +Батарея `MAKE_EXIT=0`, `0 issues`, все пакеты `ok` (`pipeline` 571 s под `-race`), скипы те же. **Голден в этом круге не трогали, и `TestGoldenDeterminism` проходит против него** — круг не сдвинул ни одного байта провода. Тесты **1156 → 1173**. Каталог **120 → 141**; **все двадцать одна новая запись предъявлена КРАСНОЙ** (`WB41` — со второй попытки, после починки фикстуры, которая случайно давала верный ответ и под посадкой). + +⚠ **КАТАЛОГ ПРОГНАН ЦЕЛИКОМ — впервые, и первый же целый прогон окупил починку инструмента.** 141 запись: **136 RED · 0 SURVIVED · 0 NOTHING · 1 записанный `survives` (`G-byte-gate`, аргументирован) · 4 ROTTED**. Протухли четыре МОИ ЖЕ записи, чьи якоря увезли рефакторы кругов 2–3: `WB5` (сборка `classifyInput` уехала в конструктор), `WB12` (правило дедупа исправлено кругом 2 — запись описывала регрессию, которую я же и починила), `WB16` (у валидатора не стало собственного списка), `WB21` (банк-роли уехали в `gateModels`). **Под прежним поведением прогон лёг бы на первой из них, и трёх остальных никто бы не увидел** — ровно то, что произошло с `AF-keys-file-reason-covers-all` и держалось незамеченным два пака. Все четыре пере-навешены на нынешние выражения тех же свойств (у `WB12` и `WB16` заодно переписан `why`: свойство изменилось, а не только адрес) и предъявлены красными. `AF` в целом прогоне — **RED**, то есть починка якоря проверена не на слово. + +## Файлы + +**Изменены:** `internal/pipeline/{disposition,chunkrun,waverun,events,manifest,mining,terminologist,repair,render,runner}.go` · `internal/membank/{memory,mempostcheck,decisions}.go` · `internal/terminology/{terminology,classify}.go` · `internal/miner/miner_emit.go` · `internal/checks/sanitizer.go` · `internal/config/{book,pipeline}.go` · `internal/lang/embedded.go` · `internal/store/migrate.go` · `cmd/tmctl/render.go` · `cmd/tmmutate/mutations.json` (92 → 96) · `configs/pipeline-{c1,c2,arm-*}.yaml` · `prompts/zh-ru/*.md` (шесть — жанр; `classifier.md` — третье поле) · `example/book.yaml` · тесты и голден. +**Новые:** `internal/langscreen/{langscreen,langscreen_test}.go` · `internal/pipeline/{cuttag_test,gender_wire_test,offtarget_corpus_test,volumepanic_test,offtargetflag_test,contourpreflight_test}.go` · `internal/pipeline/testdata/offtarget-corpus.json` · `internal/config/{bank_contour_test,genre_retired_test}.go` · `internal/checks/latinguard_test.go`. + +## Команды приёмки + +``` +cd backend && make battery # MAKE_EXIT=0, 0 issues, 19 пакетов ok, 3 скипа +go test ./internal/pipeline/ -run 'Gender' -v # производитель рода, три двери доставки +go test ./internal/pipeline/ -run 'OffTargetCorpus|LiveEchoGate|LatinOffTarget|TargetScreen|TwoScreens|ScreenConstants' -v +go test ./internal/langscreen/ -v # изолированный модуль экрана +go test ./internal/config/ -run 'BankContour|BankRoleBudgets|Genre' -v +go test ./internal/membank/ -run 'ApprovingATermKeeps|LawBlockGivesOneOrder' -v # род переживает подпись; один приказ на термин +go test ./internal/pipeline/ -run 'OffTargetCompletionIsFlagged|RunnerHandsTheScreen|ThresholdIsUsed|ContourRefusesAWriteRun|RenderRejectsUnknownPlaceholder' -v +go test ./internal/checks/ -run 'LatinResidueClassIsGated' -v # вторая половина дыры 46, обе стороны +go test ./internal/pipeline/ -run 'ACrashOverACaughtCeiling|StopLineSaysTheGrant|OneCarriedUnit' -v +cp -a backend /tmp/mut/backend && cd /tmp/mut/backend && GOFLAGS=-buildvcs=false \ + go run ./cmd/tmmutate -root . -id WB1-head-always-end-of-book,WB2-head-keyed-on-the-carry,WB3-bound-border-drops-a-single-carry,WB4-cut-tag-drops-the-embedded-data,WB5-target-screen-never-sees-the-target,WB6-off-target-flag-not-escalatable,WB7-bank-role-exemption-narrows-to-one-role,WB8-latin-residue-runs-on-a-latin-target,WB9-contour-configured-and-inert,WB10-missing-contrast-costs-a-wave,WB11-nil-pack-contour-never-runs,WB12-two-orders-for-one-term,WB13-placeholder-refusal-arrives-after-a-paid-wave +grep -rl '{{genre}}' backend/prompts/ | wc -l # 0 +grep -rn '^func Test' backend --include=*_test.go | wc -l # 1143 +python3 -c "import json;print(len(json.load(open('backend/cmd/tmmutate/mutations.json'))))" # 105 +``` + +### 04.09 — записка-план вайр-батча «род · экран цели · заголовки инъекций» (промт `BACKEND_WIREBATCH_SESSION_PROMPT.md`, сессия `textmachine-main-7e`) + +> **Скоуп — шесть пунктов промта.** (1) 210: род производится авто-путём, доказательство сквозным прогоном «произведено → отрендерилось». (2) 113 → 46 в этом порядке: корпус-фикстура и регресс на реальный запас боевого гейта, потом экран цели именованным флагом + вторая половина дыры `detectLatinInsertion`. (3) 134: единый закон-блок редактора, маркер ⟨проверить⟩ с провода, то же у транслятора. (4) 140: банковый контур в `pipeline-c1.yaml`, суб-бюджеты ×4.47. (5) 184(а): `{{genre}}` из шести промптов пары и поле из канона брифа. (6) ДОФИКС чужого пака D39.187 п.4 (две посадки + граница `bound()` + три числа) и ФЧ-7 строки 197 — ПРОВЕРКОЙ, не стройкой. +> **Инварианты.** Не коммичу — дерево на лендинг оркестратору. НЕ двигаю: схему хранилища · `manifestVersion` · `StreamVersion` · версию контракта. Новый эмбед-файл не завожу. `FlagCJKArtifact` не переиспользую (снято D39.93 п.2). Статусы строк и подписная таблица в 134 не трогаются (D39.104 п.1). Семантику переноса слота не трогаю. Текст заголовка редактора не меняю — он байт-идентичен арму `AM-noclause` пробы 18 (`experiments/18-editor-wire-probe.md` §0.3). +> **Развилки, объявленные ДО кода.** (1) род — производится КЛАССИФИКАТОРОМ (третье поле его ответа), а не терминологом и не третьим проходом: ноль новых вызовов, парсер классификатора позиционный и закрыто-словарный. (2) Второй замок (`Confirmed`-фильтр рендера рода) — вынесен пингом оркестратору на ратификацию, сам не снимаю. (3) 134 — снимаю РЕНДЕР в Go, `internal/lang/data/injection.txt` НЕ трогаю: правка данных двигает `EmbeddedVersion` → `cutTag` → `manifest_key` → снос `unit_resolutions` книги ПАРАЛЛЕЛЬНОГО платного прогона платформы. +> **Что НЕ делаю.** Чужие зоны `platform/` `frontend/` `eval/` и соседний worktree. Строки 131 · 105(а) · 14 · 119 · 124 · 115 · 160 · 161 · 142. +> **Самопроверка исполнением.** Дифф `^func Test` пофамильно до/после (удалённых имён 0) · маскированный дифф голдена (только названные версионные оси + ровно две строки диспозиций) · собственные посадки `tmmutate` ВНЕ шести пунктов на копии дерева · отдельный адверсариальный проход свежим контекстом (модель назову) · отчёт о движении `cutTag`: `tmctl manifest` до и после. + +### 02.09 — эхо-записка пака «число согласия на пути второй покупки» (промт `BACKEND_CONSENT_SESSION_PROMPT.md`, сессия `textmachine-77`) + +> **Канала к оркестратору нет:** адрес в `/tmp/textmachine-channel` (`textmachine-33`) в `ListAgents` отсутствует, промт писан на другой машине — вопросы едут секцией отчёта ниже. Дерево на входе чисто, HEAD `494c4ef`; все `file:line`-якоря промта сверены грепом на этой машине и сходятся. +> **Скоуп — четыре пункта в порядке промта:** (1) ФЧ-5, ось МОДЕЛИ: строка прайсится по модели, которую её стадия резолвит СЕЙЧАС (`Stage.ResolvedModel`); округление вверх при незнании = максимум из цен по ответившей модели, по текущей и — для эскалационных вызовов — по текущему хопу; оговорка «оценка, ошибается вверх, вот чем» в ОБОИХ текстах согласия; обещание точности снимается в `status.go` (комментарий у `RebillUSD`) и в шапке `reprice.go`. (2) Строка 232, остаток «слот»: юнит, который прежний прогон НАЧАЛ (есть строки текущих позиций) и не отгрузил по оси `finalStageWave`, слота гранта повторно не тратит — дописывается вне гранта, считается отдельно. (3) ФЧ-4 обе половины: `rebill_historical_rows` в `status --json` под тем же `rebill_basis` + basis в тексте отказа `--accept-rebill=`; одна формулировка basis для CLI-статуса и текста отказа. (4) ФЧ-1: ровно три названные гарантии — сперва воспроизвести мутацию на копии и увидеть зелень, потом посадка тестом + запись в `cmd/tmmutate/mutations.json`. +> **Инварианты:** направление ошибки — никогда не занизить молча (D39.150) · `status` и `translate` — ОДИН вывод · фигура едет с базисом (D39.181 п.1) · ось отгрузки = `finalStageWave`, не draft и не «любая» · схема хранилища, форма манифеста, версия контракта НЕ трогаются · тесты не подгоняются, заказанная смена поведения объявляется (D39.183). +> **Не делать:** строки 160 · 221 · 224 · 226 · 228 · 230 · 246 · 247 · 233 · 229 · 141-остаток · 131 · платный замер · новое поле в кадре `finished` шва (класс «поле в опубликованном документе» — ратификация оркестратора; счётчик «дописан вне гранта» живёт в отчёте прогона и логе, вопрос — в отчёт) · коммиты. +> **Самопроверка исполнением:** дифф `^func Test` (база 1083, снимок имён снят) · собственные посадки `tmmutate` на копии в scratchpad · свежий адверсариальный проход субагентами Fable 5 (число назову) · `make battery` на копии до и на дереве после. + +### 03.09 — ОТЧЁТ пака «число согласия на пути второй покупки» (сессия `textmachine-77`, промт `BACKEND_CONSENT_SESSION_PROMPT.md`) + +> **Итог:** четыре пункта состава исполнены, дерево `backend/` готово к лендингу, **НЕ КОММИЧУ** (`git diff --cached --name-only` → пусто). HEAD на входе `494c4ef`; после согласованного с платформой (`textmachine-37`) `git pull --ff-only` — **`2a63c37`**. Входящее (72 файла) не пересекалось ни с одним моим файлом, кроме `docs/PROGRESS.md`: свою записку сняла редактором, дала git пройти fast-forward, вернула. Индекс не трогала, stash не применяла. +> **Канала к оркестратору нет:** `textmachine-33` в `ListAgents` отсутствует, промт писан на другой машине — вопросы секцией ниже. Все `file:line`-якоря промта сверены грепом здесь и сошлись. +> **Батарея на финальном дереве:** `cd backend && make battery` → `MAKE_EXIT=0`, `golangci-lint` `0 issues`, `ok` в 18 пакетах, SKIP: `TestMinerFullBookParity`, `TestHelperEventsRun`, `TestHelperKillLoop`. ⚠ Скипов ТРИ, а не шесть как в базовом прогоне, и это не ослабление: `TestCheckerLabelsBaseline`/`TestCheckerLabelsCandidates`/`TestK6LabelsBaseline` в реальном дереве корпус находят и ИДУТ — `go test ./internal/checks/ -run TestCheckerLabelsBaseline -count=1 -v` → `--- PASS`, `go test ./... -run TestK6LabelsBaseline -count=1 -v` → `--- PASS`. Базовый прогон гнался на копии в `/tmp`, где маркер корня репозитория не находится. +> **Тесты `^func Test`:** `grep -rn '^func Test' backend --include=*_test.go | wc -l` → **1083 → 1105**; пофамильный дифф двух снимков (`comm -23`) — **удалённых 0**, добавленных 19. + +**Комплектность против заказа — по четырём пунктам и по таблице разметки.** + +**1. ФЧ-5 — ось МОДЕЛИ. Все ЧЕТЫРЕ обязательства D39.150 п.1.** +- **(1) прайс по модели, которую стадия резолвит СЕЙЧАС.** `internal/pipeline/reprice.go`, `currentModelFor(stage, escalation)`: для эскалационного вызова — текущий `ResolvedHop`, иначе `ResolvedModel`. **Именно РЕЗОЛВЕННЫЙ слот, а не конфигурационный `model:`** — на книге с лейблами это разные модели, и прайсить надо по той, которую прогон вызовет (пин `TestTheModelAxisReadsTheRESOLVEDSlotNotTheConfiguredOne`, каталожная запись `FC5-model-axis-configured-slot`). Признак эскалации взят из колонки `checkpoints.escalation` полем `store.CheckpointUsage.Escalation`; **схема НЕ тронута** (`git diff --stat -- backend/internal/store/migrate.go` → пусто), колонка живёт с v3. +- **(2) округление ВВЕРХ при незнании — формула моя.** `now = max( cost(токены @ модель, которая ОТВЕТИЛА), cost(токены @ модель, которую стадия резолвит сейчас) )`. Довод: количество токенов принадлежит ответившей модели, цена — той, которая ответит; ни одна не смеет опустить число ниже другой. Оба направления проверены исполнением: стадия ушла на модель ×10 — число ×10 (замер приёмки `$0.003640` против `$0.036400` воспроизведён тестом); стадия ушла на модель ÷10 — число НЕ падает. База порога `projectBookUSD` считается тем же `usd()`, поэтому обе половины гейта в одной валюте. +- **(3) оговорка в тексте согласия.** `projectionBasis` (`rebill.go`): «…an ESTIMATE that errs upward, never below what the answering model would charge for the same tokens **where their usage is on file**; K of the N unit(s) were **bought from** a model their stage no longer resolves to…». Едет в ЧЕТЫРЁХ операторских местах: пороговый отказ, его scoped-клауза, отказ по названному потолку, и `tmctl status` через `StatusReport.RebillFigureBasis()`. +- **(4) обещание точности СНЯТО в ОБОИХ живых местах, названных промтом.** `git grep -n 'can never quote a different number' -- backend/` → пусто; на входном коммите `git grep -n 'can never quote a different number' 2a63c37 -- backend/ | wc -l` → **1** ⚠ (в первой редакции отчёта стояло «3» — число было снято широким грепом на три разных шаблона и приписано этому, узкому; исправлено дофиксом); `grep -rn 'NOT projected (like the content axis' backend/internal` → пусто. Заменено: шапка `reprice.go` — «ROUTING IS PROJECTED, AND IT ROUNDS UP»; комментарий `RebillUSD` в `status.go` — «what neither of them promises is ACCURACY… an estimate that errs UPWARD **where the tokens are known**». Третья копия обещания (исторический комментарий у `projectStoredMemory`) снята попутно. +- **Решила сама, сверх буквы заказа:** счётчик `RebillProjection.ModelMovedRows` и поле `rebill_model_moved_rows` в `status --json`. Довод: фигура, часть которой — верхняя оценка через смену модели, без этого числа неотличима от точного пере-прайса, а D39.181 §2.3 требует базиса у опубликованной фигуры. Это ВТОРОЕ новое поле сверх названного промтом одного — вопрос 3. + +**2. Строка 232, вторая половина — ЗАКРЫТА ТОЛЬКО ПО ОСТАТКУ «СЛОТ».** +- **Ось отгрузки — `finalStageWave`**, как велено; не draft и не «любая». Проверена исполнением и на редакторском, и на ЧЕРНОВОМ-ТОЛЬКО конвейере (`TestDeliveryIsReadFromTheSHIPPINGWaveOnADraftOnlyPipeline`, `TestACarriedUnitOnADraftOnlyPipeline`). +- **Механизм.** Класс `unitCarried` в `volume.go`: юнит, у которого есть строка ХОТЯ БЫ ОДНОЙ позиции, исполняемой этим конвейером, но не всех, и который не отгружен по оси. Он допускается ВНЕ гранта, но платит; в отчёте — внутри `Delivered` и отдельно в `Carried`. Сравнение с потолком — `granted() = Delivered − Carried + Reworked`, заменило `Paid()` в ОБОИХ местах (`planVolume`, `rescopeEditWave`). +- ⚠ **И сам перенос ОГРАНИЧЕН ГРАНТОМ — не больше `MaxUnits` перенесённых за прогон, в порядке книги.** Без этой границы потолок переставал быть потолком: прогон без `--max-units`, начерновивший всю книгу и умерший до редакторской волны, отдавал следующей покупке ВСЕ свои юниты. Замерено на фикстуре: **грант 1 оплачивал 5 юнитов и 5 вызовов провайдера**, а поскольку в остатке не оставалось ничего, прогон не отчитывался вовсе. Найдено адверсариальным проходом по МОЕЙ ЖЕ работе. Принцип границы, а не число: покупка на N может дописать столько же прерванных юнитов, сколько начинает новых, — работа прогона остаётся пропорциональна гранту. Не влезшее держится как `LeftFresh` («никогда не доставлено»), потому что это ровно оно. +- ⚠ **`bound()` теперь истинен и когда `Carried > 0`,** а не только когда что-то удержано: прогон, дописавший юниты вне гранта, обязан это СКАЗАТЬ, иначе молчание приходится ровно на случай с наибольшим расхождением. +- **Решила сама (промт отдал):** *где вычитается потраченный слот* — нигде: он НЕ БЕРЁТСЯ. *Как переживает рестарт* — носитель «юнит начат» — его же строки `chunk_status` по текущим позициям (`unitStarted`/`unitPositionsOnFile`, одно перечисление с `unitFullyRecorded`, чтобы они не разошлись в понимании «позиции»); схемы не нужно. *Частично двинутый юнит* — carried, дописываются только недостающие позиции. +- **Замер строки исправлен:** было «4 купленных юнита → 2 главы», стало 4 → 4 (`TestAUnitInterruptedBetweenWavesTakesNoSecondSlot`: `Delivered=4 Carried=2 LeftFresh=1`, ровно 6 вызовов во второй покупке). +- **Семантика `--max-units` изменена и объявлена во всех трёх операторских местах** (`cmd/tmctl/invocation.go`, `Runner.MaxUnits`, `backend/README.md`): грант считает юниты, которые прогон БЕРЁТ СЛОТОМ — новые и пере-делываемые; перенесённые названы в строке стопа и в логе (`finished_from_an_earlier_grant`). ⚠ Тексты НЕ утверждают, что «слот за юнит был взят прежним прогоном»: движок этого не знает (прежний прогон мог идти без потолка), и первая редакция текстов это утверждала — снято. +- ⚠ **ОСТАТКИ, которые пак НЕ закрывает — названы в докстринге `deliveredUnits` и здесь:** + 1. **«первая редакторская стадия»** (остаток, который промт велел назвать): конвейер, получающий ПЕРВУЮ редакторскую стадию, двигает отгружающую волну с draft на edit, и уже прочитанные юниты репортятся свежими. Закрытие требует записи о том, ЧЕМ БЫЛА отгружающая волна. + 2. **Прерванная ПЕРЕ-ДЕЛКА — новый остаток, найденный этим паком.** Отгруженный юнит, чья пере-делка оборвана между волнами, имеет строку в КАЖДОЙ позиции, снова классифицируется `unitRework` и снова берёт слот. Отличить его от обычного банк-онли сдвига (черновик free, редактор пере-оплачивается — определение rework) можно только записью «какой прогон купил какую строку». **Строку 232 закрываю только по остатку «слот», как и велено.** + +**3. ФЧ-4 — ОБЕ половины, фигура едет с базисом (D39.181 п.1).** +- **(а)** `HistoricalRows` больше не теряется на границе `status.go`: `rebillOutcome` возвращает проекцию ЦЕЛИКОМ (под `none`/`failed` — нулевую структуру, так что новое поле не может пережить «неизвестный» ответ, забытое в одной ветке), в `StatusReport` — `rebill_historical_rows` и `rebill_model_moved_rows` под тем же `rebill_basis`. +- **(б)** отказ по названному потолку `--accept-rebill=` несёт `projectionBasis(proj)` — базис ТОЙ ЖЕ run-scoped проекции, чьё число печатает. +- **Сверх заказа, по тому же правилу «каждая цифра предложения — из его проекции»:** scoped-клауза ПОРОГОВОГО отказа печатала число этого прогона рядом с книжным базисом; теперь несёт свой (`TestTheScopedClauseOfTheRefusalCarriesItsOwnBasis`). Дефект был и до пака. +- **Один вывод на два канала:** `StatusReport.RebillFigureBasis()` → строка RE-PAYMENT в `tmctl status`; `TestABilledDecodeRowIsDisclosedAsOldMoneyEndToEnd` сверяет, что проекция, `--json`, CLI и ОБА текста отказа называют одну строку раскрытия. +- **Решила сама (промт отдал: имя поля · форма basis · что печатать при `HistoricalRows=0`).** *Имя* — `rebill_historical_rows`, в форме соседей по префиксу `rebill_*`. *Форма basis* — НЕ второе поле-строка рядом с числом, а одна формулировка `projectionBasis`, которую метод `RebillFigureBasis()` переиспользует для CLI: существующий `rebill_basis` остаётся КАЧЕСТВЕННОЙ осью (`pending`/`stored`/`none`/`failed`), а новые счётчики — количественной, и словами их описывает один текст, чтобы гейт и статус не разошлись формулировкой. *При нуле* — печатается ноль, а не пропуск поля: `omitempty` здесь тот же дефект, ради которого его сняли с `rebill_units`/`rebill_usd` — отсутствие поля неотличимо от «свободно». Ноль под `pending` значит «всё пере-оценено», под `failed` — «не считалось»; различает их `rebill_basis`. В операторском тексте при нуле клауза просто не печатается: предложение не должно нести оговорку, у которой нет предмета. + +**4. ФЧ-1 — ровно три названные гарантии, по каждой все ТРИ шага.** +- **Шаг 1 — воспроизвести на копии ДО пака.** `cp -a backend /tmp/.../mut0/backend`, `go run ./cmd/tmmutate -root . -catalog prepack_mutations.json` → **все пять посадок SURVIVED**: фильтр по книге в ОБОИХ пакетах, ключ ячейки без `chunk_idx` в ОБЕИХ формах, снятие пометки «не пере-оценена». ⇒ гарантии (2) и (3), принятые приёмкой СО СЛОВ линзы, подтверждены исполнением; ни одна не оказалась уже прибитой. +- **Шаг 2 — посадки тестом.** (1) `TestCheckpointUsageForBookIsScopedToTheBook` (две книги в одной БД, ОДНА позиция) + `TestAnotherBooksMoneyNeverEntersTheConsentNumber`. (2) `TestEveryChunkOfAChapterIsRePricedFromItsOwnCalls` — глава из нескольких чанков РАЗНОЙ стоимости: единственная форма, в которой пул ячейки виден (во всех прежних фикстурах глава = один чанк, потому мутация и переживала). (3) `TestABilledDecodeRowIsDisclosedAsOldMoneyEndToEnd` — продовый путь до `--json`, CLI и обоих отказов. +- **Шаг 3 — каталог.** `cmd/tmmutate/mutations.json`: **73 → 92**. Четыре записи `FC1-*` на три гарантии (фильтр по книге пинится в двух пакетах) плюс две `FC5-*` на несущие свойства, которые пак СОЗДАЛ (граница переноса и резолвенный слот) — их я завела по той же логике, по которой каталог существует. Денежный путь в каталоге: греп промта считает СТРОКИ — `git show 2a63c37:backend/cmd/tmmutate/mutations.json | grep -c 'reprice.go\|rebill.go\|volume.go\|ledger.go'` → **0**, тот же греп по дереву → **17**; различных ФАЙЛОВ денежного пути — **0 → 4** (`reprice.go`, `rebill.go`, `volume.go`, `store/ledger.go`). ⚠ В первой редакции стояло «стало 5 файлов» — такого числа не даёт ни одно прочтение; исправлено дофиксом. Прогон всех шести на свежей копии: **6 RED, «0 unexpected outcome(s)», exit 0**. +- Четвёртого одноразового харнесса не писала; `tmmutate` гоняла только на копиях в scratchpad. +- **Решила сама (промт отдал: форма посадок · уровень теста · текст `why`).** *Форма* — ломать ровно то утверждение, которое гарантия держит, минимальной правкой, и обязательно КОМПИЛИРУЮЩЕЙСЯ: рецепт, оставляющий переменную неиспользованной, даёт `tmmutate` вердикт «ничего не выполнялось», а не «красное», — на этом я поймала два своих же докстринга и переписала их. *Уровень* — по тому, где живёт защита, а не где живёт правка: фильтр по книге пинится и в `store`, и в `pipeline` (мутация пережила обе батареи по отдельности, значит одной мало); ключ ячейки — только `pipeline`, дефект в `newRepricer`; пометка «строка не пере-оценена» — `pipeline`, и именно ПРОДОВЫМ путём, потому что промт называл её не ловящейся на нём. *Текст `why`* — называет СВОЙСТВО и причину, по которой его не видно («во всех прежних фикстурах глава = один чанк»), а не описание правки: `why` читают в тот момент, когда мутация выжила и надо понять, что именно перестало охраняться. + +**Правки СУЩЕСТВУЮЩИХ тестов — объявляются (D39.183).** +- `TestAFlaggedMemberWithNoEditRowIsNotFree` (volume_test.go): описывал «юнит без edit-строки ОБЯЗАН потратить грант» — поведение, которое заказывает пункт 2. Гарантия «не FREE, платит и отчитывается как платящий» СОХРАНЕНА; гарантия «берёт слот» уехала в противоположную. Фикстура расширена до 4 глав; после введения границы переноса ожидание — `Delivered=2 Carried=1 LeftFresh=2`. +- `TestAnAnnouncedButFLAGGEDUnitIsStillNewBook`, `TestAUnitWhoseEDITFlaggedIsStillNewBook` (volumedelivery_test.go): флагнутый юнит после добавления стадии теперь carried. Гарантии «анонс ≠ текст» и «судить по ОТГРУЖАЮЩЕЙ строке» сохранены; фикстуры расширены до 3 глав, сигнатуры названных мутаций пере-выведены. ⚠ Рецепты мутаций в их докстрингах были НЕИСПОЛНИМЫ (правка на месте вызова оставляла `shipStages` неиспользованным, пакет переставал собираться, и `tmmutate` дал бы NOTHING вместо RED) — переписаны на правку тела `unitShipped`. +- Смена сигнатур (`repriceCheckpoint`/`usd` — третий возврат `moved`; `rebillOutcome` — проекция целиком): `TestRepriceCheckpointFallsBackToTheBilledAmount` (таблица расширена до 11 случаев), `TestRepricerCountsOnlyTheCurrentGenerationOfCheckpoints` (+4 строки), `TestAFailedBasisNeverCarriesAFigure` (сравнивает всю структуру с нулевой — стал строже), `TestRebillConsentCapBelowProjectionRefuses` (+утверждение про базис), **`TestCheckpointsOfOneGenerationSumToTheRowsCost`** (адаптирован под третий возврат `usd()`; ослабления нет, правка механическая — ⚠ в первой редакции отчёта имя пропущено, дописано дофиксом), `reprice_model_test.go` и `render_test.go` (формулировка оговорки: «were answered by» → «were bought from», см. ниже). +- `TestStatusPricesTheDriftWithTheGatesOwnNumber` — тронут ТОЛЬКО комментарий, цитировавший снятое обещание. Тест не менялся. +- **Удалённых имён — 0.** + +**Мандат самопроверки — три артефакта.** +1. **Дифф `^func Test` исполнением:** 1083 → 1102, удалённых 0, добавленных 19. +2. **Собственные адверсариальные посадки ВНЕ трёх гарантий:** каталоги `own_mutations.json` (26), `round2_mut.json` (5), `round3_mut.json` (2), `draftonly_mut.json` (2) — **35 посадок**, `tmmutate` на копиях. Итог: **все RED**. ⚠ В первом круге ДВЕ пережили, и это находки: **X21** (`rescopeEditWave` сравнивал грант через `Paid()`, то есть считал перенесённый юнит за слот и отказал бы в свободном) и **X22** (строка стадии, которой конвейер больше не исполняет, засчитывалась как «юнит начат»). Обе прибиты (`TestTheRePlanCountsSlotsNotCarriedUnits`, `TestRowsOfARetiredStageAreNotAStart`) и во втором круге RED. +3. **Проход по СВОЕЙ готовой работе свежим контекстом.** Воркфлоу из семи линз (направление ошибки · раскрытие на новой границе · слот и черновой конвейер · вывод членства `usd()` · правда текста · качество тестов · скоуп и заказ), каждая находка судится тремя независимыми опровергателями (корректность · воспроизведение · сверка с промтом), большинство решает. Модели: линза качества тестов — Fable 5, остальные — Opus 5 (ограничение владельца на число Fable-агентов соблюдено: один). **Числа прохода: 7 линз, 27 находок (2 HIGH · 12 MED · 13 LOW), 42 вердикта верификаторов, 46 агентов.** ⚠ Верификаторов «подтверждено» — НОЛЬ, и читать это надо точно: фаза верификации шла по дереву, где все 27 находок были уже исправлены, то есть опровергатели подтверждали ПРАВКИ, а не отсутствие дефектов. Каждая находка ниже воспроизведена или прочитана мной по коду до правки. + **Находки и диспозиции (все ИСПРАВЛЕНЫ, каждая с посадкой):** + - **[HIGH] перенос без границы обнулял потолок** — грант 1 оплачивал 5 юнитов, и прогон об этом молчал. Введена граница `Carried ≤ MaxUnits` + `bound()` по `Carried`; посадки `FC5-carry-unbounded` и `FC7-volume-stop-silent-outside-grant` (в первой редакции вторая называлась разведочным именем `Y2-silent-when-nothing-left`, которого в каталоге нет — переименовано дофиксом, Б7). + - **[MED] `reconcile` мог вывести `Delivered` ниже `Carried`** — `granted()` уходил в минус, строка стопа говорила «0 NEW unit(s) delivered (1 of the new ones had been started…)», предложение, которое не может быть истинным. Все ветви теперь держат вложенность; посадки `FC7-reconcile-free-branch` и `FC6-reconcile-fresh-branch` (в первой редакции стояло разведочное `Y3`, которого в каталоге нет — переименовано дофиксом, Б7). + - **[MED] операторские тексты утверждали «слот за юнит был взят прежним прогоном»** — движок этого не знает. Снято во всех трёх местах. + - **[MED] ось модели не была прибита на РЕЗОЛВЕННОМ слоте** — чтение `st.Model` вместо `st.ResolvedModel` переживало весь пакет. Пин + каталожная запись `FC5-model-axis-configured-slot`. + - **[MED] $0-строка без usage под сдвинутой стадией** публиковалась как $0 без оговорки. Теперь помечается «не удалось пере-оценить»; посадка `FC7-zero-usage-row-silent-after-a-move` (в первой редакции — разведочное `Z2`). + - **[MED] scoped-клауза порогового отказа шла без своего базиса** (дефект был и до пака) — исправлено, посадка `FC7-scoped-clause-without-basis` (в первой редакции — разведочное `X29`). + - **[LOW] ветка отката (`overshoot`) причисляла строку к «пере-прайсенным по сдвинутой модели»**, хотя цитирует БИЛЛЕННУЮ сумму, не прайсенную ни по одной сегодняшней таблице. `moved` там снят; посадка `FC7-overshoot-claims-a-model-move` (в первой редакции — разведочное `Y4`). + - **[LOW] `moved` считается против `ModelRequested`, а пять текстов говорили «модель, которая ОТВЕТИЛА»** — на провайдере, канонизирующем слаг (`gpt-5-mini` → дата), это разные вещи. Код верен (пере-прогон запросит модель стадии), тексты приведены к нему. + - **[LOW] комментарий про `zeroRow` утверждал, что хвост $0-вызовов — ОДНО поколение.** Это неверно: $0-стадия оставляет `cost_usd == 0` после каждого прогона, поколения неразличимы, и число может быть кратным. Направление при этом ратифицированное (вверх), и оговорка «errs upward» его покрывает; ложное утверждение из комментария снято, ограничение названо вслух. + - **[LOW] комментарий про redrive** утверждал, что пере-атакованный юнит «начинается заново». Неверно: `ResetChunkStages` удаляет только FLAGGED и SKIPPED стадии, черновая `ok`-строка остаётся, и юнит остаётся carried. Комментарий приведён к коду. + - **[LOW] доки кадра `finished`** (`runevents.VolumeLedger`) отправляли читателя к полю `Free` за объяснением «почему юнитов больше, чем грант», хотя теперь причина может быть и в `Delivered`. Тексты исправлены; **поля на провод НЕ добавлено** — вопрос 1. В `runevents/` изменены ТОЛЬКО комментарии (проверено: дифф без строк кода), `StreamVersion` не тронут. + - **[LOW] докстринг `CheckpointUsageForBook` ссылался на несуществующий id каталога** `FC1-book-filter` (в каталоге две записи, `-store` и `-pipeline`); `tmmutate -id` матчит точно, и следующая сессия получила бы «ничего не выбрано». Исправлено. + - **[LOW] комментарий у `RebillHistoricalRows` называл платформу потребителем поля** — её аллоулист не берёт ни одного `rebill_*`. Формулировка приведена к факту: потребитель — операторская оснастка, читающая документ; платформа поле игнорирует. + - **[LOW] несколько комментариев рассказывали ИСТОРИЮ починки** («used to», «went stale once already», «is how four bought units became two chapters») вместо описания кода — закон владельца. Переписаны; провенанс оставлен только в докстрингах ТЕСТОВ, где он в зоне принят. + - **[LOW] «первая половина» текста согласия обещала «never below…» без оговорки** про строки, которые пере-оценить не удалось: они как раз могут стоять НИЖЕ сегодняшней таблицы. Добавлено «where their usage is on file» и «may sit below today's table». + +**Три оси ревью, названные промтом уязвимыми именно в этом паке — по каждой предъявляю.** +1. **Направление ошибки.** Двойной счёт искала специально: порог сравнивает базу и сумму, посчитанные ОДНИМ `usd()` (тест сверяет `projected_book_usd`, а не только сумму), максимум из двух цен берётся РОВНО один раз на вызов, добавка денег банк-ролей входит в опубликованную проекцию и НЕ входит в базу порога — как ратифицировано. Перелёт порога проверен с обеих сторон: число выросло там, где занижало, и не упало там, где стадия ушла на дешёвую модель. Одну форму двойного счёта ревью всё же нашло — кратность оценки $0-строки; направление у неё безопасное (завышение), ограничение названо вслух и в коде, и в секции «не проверено». +2. **`HistoricalRows` на новой границе.** Не теряется и не удваивается: единственный инкремент на строку, `rebillOutcome` отдаёт проекцию целиком, под «неизвестно» — нулевую структуру. Текст отказа и `--json` называют ОДНО число с ОДНИМ базисом — это утверждает тест, сравнивающий подстроку `RebillFigureBasis()` внутри текста отказа, а не два независимых ожидания. +3. **Слот гранта на черновом-только конвейере.** Проверено исполнением, а не рассуждением: `TestACarriedUnitOnADraftOnlyPipeline` — ось не выродилась в «никогда ничего не отгружено» (юнит, начатый и не отгруженный, опознаётся и дописывается), и не выродилась в обратную сторону (уже отгруженные не пере-продаются — `TestDeliveryIsReadFromTheSHIPPINGWaveOnADraftOnlyPipeline`). + +**Что НЕ удалось и что НЕ проверено.** +- **Остаток «первая редакторская стадия»** — вне скоупа, требует носителя прошлой отгружающей волны (схема). +- **Остаток «прерванная пере-делка»** — найден этим паком, требует записи «какой прогон купил какую строку» (схема). +- **Кратность оценки $0-строки:** книга, у которой $0-стадия пере-куплена N раз, а потом уведена на платную модель, получает оценку, кратную N. Направление безопасное (завышение) и объявлено в тексте согласия, но точным его не сделать без маркера поколения на `checkpoints` — та же схемная развилка, что в докстринге `usd()`. +- **Юнит, начатый НЕОГРАНИЧЕННЫМ прогоном,** слота в следующем ограниченном не берёт (но перенос ограничен грантом). Замерено тестом, на живой книге не проверялось. +- **Взаимодействие переноса с ДЕНЕЖНЫМ потолком** на длинном хвосте начатых юнитов не замерено: по построению объёмный потолок их не считает, держит денежный. +- **Живой прогон с платными вызовами не делался** — промт его не заказывал. Вендор-сверок нет: пак не трогает слаги моделей. + +**⚠ Якоря в ЧУЖИХ доках, уехавшие ИЗ-ЗА пака — править не могу, зона не моя; вот готовый список с новыми номерами** (`python3 docs/scripts/counts.py --lint`; все шесть были ЦЕЛЫ на HEAD `2a63c37`, проверено `git show HEAD:<файл> | sed -n 'p'`): + +| док:строка | якорь был | цель сейчас | +|---|---|---| +| `docs/architecture/15-money-path.md:18` | `store/ledger.go:381` «SpentUSD reports (committed, reserved)» | `:392` | +| `docs/architecture/15-money-path.md:24` | `pipeline/status.go:290` «book_ceiling_usd,omitempty» | `:311` | +| `docs/architecture/15-money-path.md:24` | `pipeline/status.go:788` «100 * (committed + reserved)» | `:811` | +| `docs/PROGRESS.md:179` (тело строки 232) | `pipeline/status.go:866` «IT IS NO LONGER THE READ PATH'S FIRST ANSWER» | `:901` | +| `docs/archive/prompts/BACKEND_CONSENT_SESSION_PROMPT_2026-09-02.md` (строка 52 на момент выдачи; в архиве номера сдвинул баннер исхода) | `pipeline/status.go:217` «can never quote a different number» | **снят по заказу (ФЧ-5 п.4)** | +| `docs/archive/prompts/BACKEND_CONSENT_SESSION_PROMPT_2026-09-02.md` (строка 54 на момент выдачи) | `pipeline/reprice.go:24` «NOT projected (like the content axis» | **снят по заказу (ФЧ-5 п.4)** | + +Первые четыре — сдвиг номеров от моих вставок; последние два промт заякорил на тексте, который сам же велел снять, поэтому их «поломка» и есть признак исполнения. + +⚠ **Про остальные 12 я сперва написала здесь «все в `docs/experiments/**`, пришли с полигонным мержем» — это было неверно, и поправлено разбором с платформенной сессией.** Перечисляю по владению; ⚠ **общее число гейта — величина ПОДВИЖНАЯ**, обе смены правят дерево параллельно: на момент того разбора `python3 docs/scripts/counts.py --lint` печатал 18, на момент сдачи дофикса — **20** (в отчёте пака стояло «все 18 строк», и это было верно только для того часа). Разбивка по владению взята с той, 18-строчной выдачи: +- **4 — платформенной сессии** (`textmachine-37`): `docs/PROGRESS.md:19`, `docs/archive/prompts/PLATFORM_P13_SESSION_PROMPT_2026-09-02.md` (строка 286 на момент выдачи) и `docs/archive/prompts/BACKEND_CONSENT_SESSION_PROMPT_2026-09-02.md` (строка 227 на момент выдачи) целятся в одну снятую ею строку рантбука `platform/deploy/README.md:213` «сначала платформа, потом движок»; плюс `docs/archive/prompts/PLATFORM_P13_SESSION_PROMPT_2026-09-02.md` (строка 166 на момент выдачи) на уехавший `Quarantine`. ⚠ Третий из них живёт В МОЁМ промте (это его ⛔-оговорка о том, что совет рантбука опасен), но сломан не мной — целью владеет платформа. +- **8 — полигонных**, пришли с мержем `2a63c37`: семь в `docs/experiments/**` и один в теле бэклога — `docs/PROGRESS.md:190` на `eval/conformance.py:48`. +- ⚠ Якорь в `docs/experiments/23-editor-tier.md:6196` целится в строку 679 этого журнала, которой в нём нет: он был мёртв и до пака (тогда в файле было 472 строки), а мои вставки лишь меняют общее число, которым гейт объясняет смерть. ⚠ Записан здесь СЛОВАМИ, без токена «путь:номер» в обратных кавычках: гейт читает такой токен как якорь, и первая редакция этой строки сама добавила себе двадцатую поломку. + +**Урок, который стоит записи:** первую версию этого абзаца я построила на подсчёте, не перечислив предмет, — тот же класс, что и мои прошлые ошибки «вывести отсутствие из счёта». Счёт по подстроке пути (`grep -c 'platform/'`) тоже не есть счёт по владению: у платформы один из четырёх якорей целится ОТНОСИТЕЛЬНЫМ путём и в такой счёт не попадал. + +**Вопросы, вынесенные владельцу, и его слово (03.09) — записываю, чтобы решения не пере-открывались.** +1. **Поле `carried` в кадре `finished` шва** (`runevents.VolumeLedger`). Спрошено: добавлять ли число «сколько глав дописано сверх гранта», ведь `delivered` теперь может превышать `max_units`. Цена вопроса названа: это класс «новое поле в опубликованном документе» — ратификация оркестратора плюс минорный бамп `StreamVersion`, а промт запретил двигать контракт; платформа кадр `Volume` сегодня не декодирует вовсе (`platform/internal/ingest/events.go`: `Finished{Outcome}`), потребителя у факта нет. **РЕШЕНИЕ ВЛАДЕЛЬЦА: оставить как есть, поле не добавлять.** Исполнено: поля нет, объяснение живёт словами в докстрингах `Delivered` и `Free` того же кадра. +2. **Семантика «юнит, начатый прогоном БЕЗ объёмного потолка, слота в следующей покупке не берёт; перенос ограничен размером гранта».** Альтернативы названы: тратить на такие юниты купленное (тогда покупатель платит за главу дважды — ровно вред строки 232) либо завести схемный носитель «какая покупка оплатила какую строку». **СЛОВО ВЛАДЕЛЬЦА: вынести оркестратору, решение отложено — «подумаем на этот счёт».** ⇒ пункт остаётся ОТКРЫТЫМ вопросом к приёмке; в коде сейчас первый вариант, ограниченный грантом. +3. **`rebill_model_moved_rows`** — второе новое поле `status --json` сверх названного промтом одного; говорит, какая часть суммы завышена из-за смены модели стадии. Аддитивно, платформой игнорируется (её аллоулист не берёт ни одного `rebill_*`); без него человекочитаемая строка и текст отказа перестают говорить дословно одно и то же. **РЕШЕНИЕ ВЛАДЕЛЬЦА: оставить.** +4. **Ошибок в промте не нашла.** + +### 03.09 — ДОФИКС по приёмке №22 (наряд `docs/archive/prompts/BACKEND_DOFIX_2026-09-03.md`, сессия `textmachine-77`) + +> **Состояние git на входе в дофикс — важнее прочего, потому что наряд писан по другому.** Наряд говорит «в индексе лежит незакоммиченная работа двух смен». К моменту исполнения владелец уже закоммитил её в ЛОКАЛЬНЫЙ `e85295d` «temp», а `origin/main` был обновлён ПРИНУДИТЕЛЬНО на `ea54355`, в историю которого `temp` не входит (`git merge-base --is-ancestor e85295d origin/main` → нет). По слову владельца я откатил temp и подтянул наряды двумя командами: `git reset --mixed 2a63c37`, затем `git merge --ff-only origin/main` (перемотка — `ea54355` растёт из того же `2a63c37`). Перед этим поставил метку **`temp-backup-e85295d`**, чтобы коммит с работой обеих смен нельзя было потерять; снимать её до лендинга не надо. Целостность проверена, а не предположена: изменённые файлы совпали с temp **бит-в-бит** (сравнение хешей двух диффов), пять добавленных файлов снова стали неотслеживаемыми и лежат на диске побайтно как в коммите (`git hash-object` против `git rev-parse e85295d:<файл>`). В дереве НА МОМЕНТ ОТКАТА было 44 файла: 22 мои, 21 платформы, один общий журнал; дальше число растёт у обеих смен по мере работы. **Индекс пуст, не коммичу.** +> +> **Батарея после дофикса:** `cd backend && make battery` → `MAKE_EXIT=0`, `golangci-lint` ``0 issues``, `ok` в 18 пакетах, SKIP: `TestMinerFullBookParity`, `TestHelperEventsRun`, `TestHelperKillLoop`. +> **Тесты `^func Test`:** `grep -rn '^func Test' backend --include=*_test.go | wc -l` → **1083 → 1105** (пак довёл до 1102, дофикс и правки по проверяющему добавили остальное), удалённых **0** (`comm -23` по снимкам имён). +> **Каталог `cmd/tmmutate/mutations.json`:** **73 → 92** (`python3 -c "import json;print(len(json.load(open('backend/cmd/tmmutate/mutations.json'))))"`), прирост **19** записей: паком **6** (`FC1`×4 — три названные гарантии, `FC5`×2 — свойства, которые пак создал), дофиксом и разбором его проверки **13** (`FC6`×4, `FC7`×5, `FC8`×3). ⚠ В первой редакции этой строки стояло «18 заведено дофиксом и проверкой», а перечень в скобке давал 14 и не называл `FC1` — число одной величины под именем другой, тот же класс, что чинит Б8. Команда: `python3 -c "import json;from collections import Counter;d=json.load(open('backend/cmd/tmmutate/mutations.json'));print(Counter(m['id'].split('-')[0] for m in d if m['id'][:3] in ('FC1','FC5','FC6','FC7','FC8')))"`. + +**По каждому пункту Б1–Б9.** + +**Б1 — стоп-строка и лог лгали в случае, который пак создал. ЗАКРЫТО.** +Воспроизвёл сам, до правки: `VolumeStop{MaxUnits:2, Delivered:2, Carried:2}` → `Left()=0 granted()=0 Paid()=2`, и предложение печатало разом «not on money and **not at the end of the book**» и «Still in the book: **0** unit(s) NEVER delivered». +Правка: у `String()` теперь ДВА открытия по `Left()`. Грант придержал работу → прежний текст. Книга кончилась → «reached the END of the book under a VOLUME ceiling (--max-units N), which held nothing back», и хвост «Nothing is left in the book» вместо перечисления нулей. Смешанный случай отдельного текста не требует: это первая ветка с уже существующей клаузой про перенесённые. Тот же развилкой пошёл лог `waverun.go` (сообщение выбирается тем же предикатом), а протухший комментарий у `scope.bound()` пере-написан на то, что код делает: «set when the grant held work back OR when the run did work outside it». +Пин: `TestARunThatWorkedOutsideItsGrantSaysSo` дополнен — требует и «reached the END of the book», и «Nothing is left in the book», и ЗАПРЕЩАЕТ «not at the end of the book». + +**Б2 — ратифицированный контракт кадра `finished.volume` стал ложным. ЗАКРЫТО, поля НЕ добавлял.** +Утверждение «present only when a volume grant actually held work back» снято во всех ЧЕТЫРЁХ местах: `runevents.go` (объявление поля и абзац «its PRESENCE is the boolean»), `events.go` (`volumeLedger`), `bookrun.go` (`BookResult.Volume`). Проверка: `grep -rn 'only when the grant actually held something back\|present only when a volume grant actually held\|nil when no grant held anything back\|rather than because the book ended' backend/internal` → пусто. +Новая формулировка говорит, что присутствие значит «грант был в силе и прогону есть что о нём сказать», и что различают два случая `left_fresh`/`left_rework` — оба ноля значат «книга кончилась». Там же прямо сказано, что счётчика перенесённых на проводе НЕТ и почему: владелец 03.09 сказал «оставить как есть». Ратификационная ссылка сохранена с оговоркой, что правило присутствия расширилось вместе со строкой 232. + +**Б3 — три свойства не были прибиты. ЗАКРЫТО, каждое воспроизведено и посажено.** +Сначала воспроизвёл все три на копии — **все три SURVIVED**, как и писала приёмка. После правки те же три посадки на свежей копии → **все три RED**. +- **(а)** ветка `unitFresh` в `reconcile`: новый `TestReconcileKeepsContainmentOnTheFreshBranch`. ⚠ Состояние в нём СКОНСТРУИРОВАНО, а не наблюдено, и это сказано в докстринге: `Delivered == Carried` с исходом класса `unitFresh` не даёт сегодня ни одна фикстура. Утверждаю инвариант всё равно, потому что код заявляет его для КАЖДОЙ ветки. +- **(б)** отказ по названному потолку: `TestTheScopedClauseOfTheRefusalCarriesItsOwnBasis` дополнен половиной про `--accept-rebill=` — требует «1 of the 1» и запрещает «2 of the 2». +- **(в)** оговорка про смену модели при нуле: `TestProjectionBasisNamesWhatTheAmountIsMadeOf` получил отрицательный пин, симметричный тому, что был у близнеца про старые деньги. +Записи каталога: `FC6-reconcile-fresh-branch`, `FC6-capped-refusal-borrows-book-basis`, `FC6-model-caveat-printed-at-zero`. + +**Б4 — докстринг `classifyUnits` не пере-считан. ЗАКРЫТО.** «three things this run can do to it» → «four»; проверено, что членов `unitClass` действительно четыре (`sed -n '/^const (/,/^)/p' … | grep -c '^\tunit'` → 4). + +**Б5 — пример в `README.md:73` противоречил новой семантике. ЗАКРЫТО.** «оплатить не больше 10 выходных юнитов» → «не больше 10 выходных юнитов возьмут слот», в согласии с телом README:11. + +**Б6 — шов `store → репрайсер` прибит с одной стороны. ВЗЯЛ, хотя пункт «по желанию».** +Довод: мой же аргумент по фильтру книги («мутация пережила обе батареи по отдельности, значит одной мало») применим и здесь, и отказ от него был бы непоследовательностью, а не решением. Новый `TestCheckpointUsageCarriesTheEscalationFlag` в `internal/store` + запись `FC6-escalation-flag-store`; подмена `c.escalation` на `0` теперь краснеет и в store-пакете. + +**Б7 — отчёт ссылался на ПЯТЬ несуществующих посадок как на живые гейты. ЗАКРЫТО заведением, а не снятием ссылок.** +Проверил каждую из пяти на копии: **все пять ловятся существующими тестами** — то есть свойства охраняются, но каталог их не помнил, а отчёт называл гейтами. Развязка честная только одна: сделать их настоящими записями. Заведены `FC7-volume-stop-silent-outside-grant`, `FC7-reconcile-free-branch`, `FC7-zero-usage-row-silent-after-a-move`, `FC7-scoped-clause-without-basis`, `FC7-overshoot-claims-a-model-move`; все пять пере-посажены → RED. + +**Б8 — четыре числа не печатались командами, которые отчёт для них называл. ЗАКРЫТО, каждое пере-считано.** +- «денежного пути стало 5 файлов» — такого числа не даёт ни одно прочтение. Правда: греп промта считает СТРОКИ, `0 → 17`; различных ФАЙЛОВ денежного пути `0 → 4`. Обе команды вписаны рядом с числами. +- «на входе 3 попадания» у грепа про снятое обещание — правда **1**: `git grep -n 'can never quote a different number' 2a63c37 -- backend/ | wc -l` → 1. Тройка была снята широким грепом на ТРИ разных шаблона и приписана этому, узкому. Тот же класс, что и мои прошлые ошибки со счётом: число из одной команды под именем другой. +- «все 18 строк» линта — на момент сдачи дофикса гейт печатает **20** (`python3 docs/scripts/counts.py --lint`). Число ПОДВИЖНОЕ: параллельная платформенная смена правит свои файлы, и её якоря уходят и возвращаются. В отчёт пака вписана оговорка об этом. +- «в файле 485 строк» — сейчас **572** (`wc -l < docs/PROGRESS.md`); число снималось до того, как отчёт дорос, и с каждой вставкой меняется. Формулировка в отчёте пака переписана так, чтобы не зависеть от него. +- ⚠ **Отчёт пака сам добавлял себе поломку гейта:** строка таблицы называла чужой мёртвый якорь ТОКЕНОМ «путь-двоеточие-номер» в обратных кавычках, и гейт читал её как якорь ЭТОГО журнала. Переписана словами. ⚠ И первая редакция этого самого пункта наступила на то же место второй раз — объясняя поломку, она снова процитировала токен и снова его создала. Теперь ни одна строка обеих моих записей токена не содержит; проверка — грепом по выдаче гейта на строки этого журнала в моих секциях, пусто. + +**Б9 — неполнота самообъявления по D39.183. ЗАКРЫТО.** `TestCheckpointsOfOneGenerationSumToTheRowsCost` дописан в блок самообъявления: адаптирован под третий возврат `usd()`, ослабления нет, правка механическая. + +**Б10 — к сведению, не заказ; называю, чтобы не умерло молча.** +- **Подсказка в докстринге `Delivered` ключуется на `delivered > max_units`,** а в форме «весь остаток перенесён» `delivered == max_units` — подсказка не срабатывает, и разложить кадр читателю нечем. Связано с отложенным вопросом владельца о семантике переноса; чинится вместе с ним. +- **В перенос попадает и юнит, который ФЛАГНУЛСЯ и текста не отдал:** он «начат и не отгружен», значит следующая покупка снова платит за его пере-атаку вне гранта. Цикл из трёх последовательных покупок не гонял никто — ни приёмка, ни я. Вход в решение владельца, не дефект. + +**Проверка дофикса свежим контекстом — один агент Fable 5, ограничение владельца соблюдено.** Он прочитал исходный промт, наряд, оба моих отчёта и весь дифф, посадил 26 своих мутаций и пере-гнал батарею на копиях. Из его семи находок **три взяты в работу и исправлены**, четыре названы ниже как известные пробелы: +- **[HIGH] `$0`-вызов ВНУТРИ платного поколения выпадал из обхода** — и уносил с собой сегодняшнюю цену. Форма: черновая стадия на локальной `$0`-модели с ПЛАТНЫМ `escalate_to`; стоимость строки объясняет один хоп, поэтому обход останавливался и бесплатный primary терялся, а после сдвига стадии на платную модель число согласия выходило вдвое ниже реальной пере-покупки, БЕЗ оговорки. Воспроизвёл сам на юнит-уровне до правки: `usd=0.010000 fromHistory=false moved=false` при пере-покупке `0.03`. Правило переписано: **вызов, стоивший ноль, никогда не останавливает обход и никогда не переполняет счёт** — он берётся всегда, а обход стоит на первом ПЛАТНОМ вызове после того, как деньги строки объяснены. Цена решения названа в коде вслух: бесплатный вызов на хвосте СТАРОГО поколения тоже будет взят, то есть оценка завышается — ратифицированное направление. Посадка `FC8-free-call-dropped-from-a-paid-generation`. +- **[LOW→взято] `$0`-строка, чей новейший вызов чужой,** котировалась `$0` без оговорки: переполнения на нуле не бывает, поэтому детектор отката её не видел. Теперь такая строка помечается «не удалось пере-оценить»; `skipped`-строка остаётся честно бесплатной. Посадка `FC8-unestablished-zero-row-published-as-free`. ⚠ Существующий случай таблицы, чей комментарий говорил «skipped-строка», диспозицию не ставил — привёл тест к его собственному замыслу и добавил случай без скипа (объявляю по D39.183: поведение сменилось по находке, тест стал строже). +- **[LOW→взято] развилка ЛОГА из Б1 не была прибита** — мутация «удалить ветку конца книги» переживала весь пакет. Новый `TestTheRunLogSaysWhichKindOfVolumeStopItWas` (обе ветки, живым прогоном), посадка `FC8-log-fork-at-the-end-of-the-book`. +- **[MED→исправлено] отчёт пака называл пять посадок разведочными именами** (`Y2…`, `Y3`, `Z2`, `X29`, `Y4`), которых в каталоге нет: Б7 завёл их под именами `FC7-*`, но ссылки остались старыми. Переименованы; `grep` по каталогу на старые имена → 0, ни одно из них больше не стоит в отчёте как имя гейта. + +**Второй проход того же проверяющего — по трём осям, вердикт его словами.** Дерево ему было пере-сдано после правок; он адресно пере-атаковал свои находки 1, 3 и 6 (все три ДЕРЖАТ, каталожные посадки красные), отдельно проверил новое правило обхода `usd()` таблицей краевых форм (откат, остаток, эскалационный хоп, `skipped`, нулевая стоимость) и занижений не нашёл. **A — исходный промт: исполнено с остатком** (остатки — два схемных остатка строки 232 и отложенный вопрос владельца, все названы и заказаны такими). **B — наряд Б1–Б9: все девять закрыты** (Б8 был закрыт с остатком, см. N1 ниже — закрыт). **C — код и отчёты: ГОДНО К ПРИЁМКЕ, блокеров нет.** Три его новые находки, все LOW, закрыты здесь же: +- **N1** — строка отчёта говорила «18 заведено дофиксом и проверкой», а раскладка в скобке давала 14 и не называла `FC1`. Правда: прирост 18 = паком 6 (`FC1`×4, `FC5`×2) + дофиксом и разбором проверки 12 (`FC6`×4, `FC7`×5, `FC8`×3). Тот же класс, что чинит Б8, — число одной величины под именем другой; пере-считано командой, она стоит рядом. +- **N2** — условие `len(calls) > 0` в метке «поколение не установлено» не было прибито: мутация переживала пакет. Добавлен случай «`$0`-строка без единого вызова на файле свободна, а не нераскрыта» + запись `FC8-empty-cell-disclosed-as-unestablished` (красная). +- **N3** — комментарий у той же ветки описывал `skipped`-строку так, будто guard несущий, тогда как продовые вызывающие skipped-строки сюда не доводят (`projectRebill` их отфильтровывает, `projectBookUSD` роняет флаг). Комментарий говорит теперь ровно это: guard защитный, а не несущий, и почему он остаётся. +Его диспозицию по находкам 4 и 5 («согласен, входы в отложенный вопрос владельца») принимаю; он же заметил, что находку 4 можно закрыть и без смены семантики — снимком plan-time `Carried`. Это ВЫБОР, не необходимость, и он входит в тот же вопрос владельца. + +**Что НЕ удалось и что НЕ проверено (дофикс).** +- **Цикл из трёх покупок на флагнутом юните** не замерен — см. Б10. Знаю, что не проверено; не чинил, потому что наряд прямо запретил трогать семантику переноса. +- **Состояние `Delivered == Carried` с исходом класса `unitFresh`** (Б3а) ни одной фикстурой не достигается; инвариант утверждён на сконструированном состоянии, и это сказано в самом тесте. +- **Живого прогона с платными вызовами не было** ни в паке, ни в дофиксе. +- **Оба остатка строки 232** (первая редакторская стадия; прерванная пере-делка) по-прежнему открыты и требуют схемного носителя. +- **Четыре находки проверяющего названы, но НЕ чинятся — все четыре суть входы в отложенный владельцем вопрос о семантике переноса, и наряд прямо запретил её трогать:** + 1. Клауза «finished OUTSIDE this grant» ключуется на `Carried` ПОСЛЕ `reconcile`, поэтому исчезает, когда все перенесённые юниты флагнулись: оператор видит «2 PAID FOR BUT FLAGGED», но не то, что обе оплаты шли вне гранта. Воспроизведено: `VolumeStop{MaxUnits:2, Delivered:2, Carried:2}` + два флага класса `unitCarried`. + 2. Перенесённый юнит с ПРОТУХШИМИ черновыми строками пере-покупает всю цепочку вне гранта, тогда как хелп флага обещает «дописать недостающее». Воспроизведено проверяющим: `granted()=0`, два вызова провайдера на один юнит. + 3. Подсказка в докстринге `Delivered` ключуется на `delivered > max_units` и молчит в форме «весь остаток перенесён», где `delivered == max_units` (это же Б10 наряда). + 4. Цикл из ТРЁХ последовательных покупок на флагнутом юните не гонял никто — ни приёмка, ни проверяющий, ни я. +- **Выживание трёх гарантий ФЧ-1 на дереве ДО пака** проверяющий сам не воспроизводил (принял по моим красным посадкам); я это делал в паке отдельно, лог в scratch-каталоге сессии. + +### 05.09 — ОТЧЁТ пака «денежный стоп перестаёт быть аварией» (сессия `textmachine-main-be`, промт `BACKEND_MONEYSTOP_SESSION_PROMPT.md`) + +> **ПОЛНЫЙ ОТЧЁТ — `backend/docs/MONEYSTOP_REPORT.md`** (происхождение решений, пять рисков зоны по +> одному, самопроверка, объявления по D39.183, obstacle reporting). Здесь — итог и то, что нужно +> оркестратору для лендинга. **Дерево НЕ закоммичено.** +> +> ⚠ **В дереве лежат ЧУЖИЕ правки — вся `platform/`** (параллельный пак `textmachine-main-63`). Не +> тронуты ни байтом. **Платного прогона НЕТ**: все три оси предъявлены за $0. + +**Четыре пункта закрыты.** (1) строка **271** — правки банка больше не могут писать свои строки внутрь +system-сообщения платного вызова: одна граница (`internal/membank/wirefence.go`), три точки исполнения — +дверь отказывает, загрузчик сида отказывает, рендереры пропускают; предел 200 рун, запрет Cc + U+2028/9 + +bidi-переопределений (Trojan Source), при этом ZWJ/ZWNJ/LRM/RLM РАЗРЕШЕНЫ (общность §0.1); фолд версии +СКОУПНЫЙ, чистая книга хеширует байт-в-байт как до фикса — запинено значением, снятым сборкой `32be78a`. +(2) строка **277** — отказ резерва защёлкивает волну и не отменяет допущенное; отказ не окончателен при +непустом полёте (ждём settle, повторяем тот же резерв); только ОБЯЗАТЕЛЬНЫЕ вызовы ждут. (3) строка +**278** (событие) — недостача едет в `ceiling`, `StreamVersion` 1.2 → 1.3, денежный леджер в `finished`. +(4) строка **278** (проекция) — цена книги ДО покупки в `manifest --json` и `status --json`. + +**Числа, каждое получено командой (полный список — §6.7 отчёта).** ⚠⚠ **ТРИ ИЗ НИХ ПРОТУХЛИ ЧЕРЕЗ ТРИ ЧАСА, И ОДНО ПЕРЕВЁРНУТО — поправка в конце абзаца; она найдена ВЕРИФИКАТОРОМ 05.09, не зоной и не мной:** тестовых функций **1172 → 1208 (+36)**, все тридцать шесть в трёх новых файлах; `git diff -U0 -- backend | grep -E '^[+-]func Test'` ПУСТ — +ни одна существующая не удалена и не переименована, два протухших починены внутри тела. Каталог мутаций **158 → 191**. Батарея: `MAKE_EXIT=0`, 19 пакетов, 0 упавших, 0 issues (шестой прогон — первые пять красны или остановлены). Каталог ЦЕЛИКОМ: **191 запись, 187 RED, 0 SURVIVED, 0 NOTHING, 0 ROTTED, 0 неожиданных исходов** — второй полный прогон; ПЕРВЫЙ не сошёлся и нашёл четыре дефекта в моих же артефактах, включая named-артефакт приёмки (§6.5 отчёта) (и `go vet -vettool` +собственного анализатора зоны — чисто; он поймал у меня один хенд-билд `config.Stage`, см. отчёт). + +⚠⚠ **ПОПРАВКА 05.09 (замер верификатора по дереву на `d766007`; числа выше НЕ переписаны — они были верны в свою минуту).** Абзац заленджен коммитом `ba4fd34` через **девять секунд** после пака `81a89e9` и за **три с лишним часа ДО** дофикса `616a8e4`, который добавил ещё +5 тест-функций и +10 записей каталога. Сообщение коммита подписало их «final counts», и после дофикса их никто не тронул. Верно на сегодня: тестовых функций **1172 → 1213 (+41)** · каталог мутаций **158 → 201** · каталог целиком — **201 запись, 197 RED, ЧЕТЫРЕ аргументированных выживших, 0 неожиданных исходов**. ⛔ **⚠ **Испр. 06.09 автором находки: ПРОТУХЛИ ДВА числа, не три, и „0 SURVIVED" НЕ протухло.** У инструмента ДВА ведра: `SURVIVED` — **неожиданный** выживший (настоящая дыра), `survives` строчными — записанный и аргументированный. На ревизии, которую абзац описывает, каталог был 191 = 187 red + 4 survives, то есть все три числа верны ДЛЯ СВОЕЙ РЕВИЗИИ; четыре аргументированных были четырьмя уже тогда. Протухли 191→201 и 187→197. Третье — не протухание, а НЕПОЛНОЕ ПЕРЕЧИСЛЕНИЕ: 187+0+0+0 = 187 при собственном итоге 191, потому что ведро аргументированных не названо. Дефект другой природы и слабее. Прежняя редакция: **Переворачивается именно последнее: «0 SURVIVED» против четырёх записанных выживших** — а это то самое различение из `D39.202` п.3 (`SURVIVED` как СООБЩЕНИЕ О НЕДОСТИЖИМОСТИ, а не как провал), и следующая движковая смена открывает этот журнал РАНЬШЕ акта. Акт `D39.206` всё это время нёс верные числа — разошлись НОСИТЕЛИ, а не факт: класс «поправил читателя, оставил писателя» (`D39.202` п.1). + +⛔ **ТРИ ВЕЩИ, КОТОРЫЕ ОРКЕСТРАТОРУ НАДО ЗНАТЬ ДЛЯ ЛЕНДИНГА:** +1. **Наружу едет НЕ то число, что назвал промт, и это согласовано.** §4(3) назвал `denied estimate` и + следующей фразой запретил его класс («цены вызовов не выходят»). Едет **недостача** + `committed+reserved+estimate−ceiling`, поле `shortfall_micro_usd` (строка 278 называла + `next_step_micro_usd`). Две оговорки в доке поля: при непустом полёте это ВЕРХНЯЯ граница; для + `day`-скоупа поле ОПУЩЕНО (дневной потолок суммирует все книги стора, движковые цифры — одной). +2. **Третье лечение пункта 1 («обрамление как ДАННЫХ») в ТЕКСТОВОЙ форме невыполнимо.** + `lang/data/injection.txt` побайтно входит в `EmbeddedVersion()` → `cutTag` → **внутрь каждого + `unit.id`**: добавление одной строки пере-чеканило бы id всех книг. Сделано структурно. +3. **`res.Volume` при денежном стопе НЕ прикладывается.** Семь счётчиков `VolumeStop` — план; денежный + стоп оставляет допущенные-и-не-доделанные юниты посчитанными как `Delivered`, а `LeftFresh` заниженным + на них, и при `Left()==0` прогон, вставший посреди книги, объявил бы «дошёл до конца». Восьмого бакета + нет ⇒ у денежного стопа СВОЙ леджер `Finished.Money{units_resolved, units_deferred}` из тех же + счётчиков, что и `progress`. + +⚠ **ЧЕТВЁРТОЕ ДЛЯ ЛЕНДИНГА, и это вход в СТРОКУ 283, а не мой дефект: `structure: detected` сегодня +CJK-ONLY.** Сопоставление заголовков в интейке идёт по `lang.DefaultCJKSection()` — КОНСТАНТА (章节節回 + +класс CJK-числительных), не данные пары. ⇒ книга в любой другой письменности возвращает `none`, как бы +явно её главы ни были размечены. Асимметрию пак НЕ вносит — движок уже читает данные пары для РЕНДЕРА +заголовка (`configs/langpacks/*/heading.txt`), а РАЗРЕЗ остаётся захардкоженным, и строка **283** называет +именно этот раскол работой. Что поле ДОБАВЛЯЕТ: у асимметрии появляется имя на проводе вместо того, чтобы +прятаться в числе глав — не-CJK книга честно говорит `none`, и форма заказа (D39.196) не предложит +ползунок в ГЛАВАХ против разреза, которого никто не делал. Классификация сама пар-агностична (спрашивает, +какой ПУТЬ провёл границы) и скажет `detected` без правки Go в день, когда разрез научится читать пару. +⚠ Первую редакцию комментария и отчёта я написала как «пар-агностично по построению» — это было +пере-утверждение, поймано собственным проходом и исправлено в обоих местах. + +**ПРАВКИ ЧУЖИХ ТЕСТОВ — объявление по D39.183 (все три вызваны сменой поведения, заказанной паком):** +· `runevents_test.go` — пин «the ceiling event must carry the fact and no figures» СУЖЕН, не снят: белый +список ключей (`halted|scope|shortfall_micro_usd`), запрет любого `$`, ПЛЮС новое утверждение — book-стоп +обязан назвать недостачу, day-стоп обязан её не называть. ⚠ Фраза ратифицирована (D39.106 §2/ПТ-33) — +ноту о сужении берёт на себя оркестратор. +· `wavepanic_test.go`, `volumepanic_test.go` — оба ВИСЛИ (`panic: test timed out after 1m30s`): парковали +падающий воркер на `<-ctx.Done()`, которую вызывал ПОТОЛОК. Потолок больше не отменяет — это заказ пака. +Триггер заменён, утверждения не ослаблены. + +**ПРАВКА ВНЕ ЗАКАЗА, взятая по своему усмотрению:** течь резервации на ветке `json.Marshal(resp.Usage)` +(`stagerun.go`) — возврат без release и без settle. Довод: денежный путь, и у него появился второй +читатель — утёкшая резервация ЗАВЫШАЕТ публикуемую недостачу. Ветка практически недостижима, поэтому её +посадка занесена аргументированным выжившим, а не находкой. + +⚠ **ЧЕТВЁРТЫЙ КЛАСС ЛОЖНОЙ ЗЕЛЕНИ, найденный в этой смене (общий, не про мой пак):** `t.Fatalf` из +ПОРОЖДЁННОЙ горутины — `runtime.Goexit` не на той горутине: утверждение теряется, прогон висит до +таймаута ПАКЕТА. **Посадка, чей отказ выглядит как таймаут, неотличима от нагрузки и потому не является +посадкой** — а `Makefile` зоны сам документирует разброс `internal/pipeline` 95 → 560 с под параллельными +сессиями. Рядом с тремя известными: упавшая сборка, вырожденное утверждение, эквивалентная посадка. + +**ГОТОВЫЙ ТЕКСТ СТРОКИ БЭКЛОГА** (таблица — зона оркестратора, поэтому текст лежит здесь, а заводит его +он; уже принят и заведён как строка **291**): + +> **Отказ РЕЗЕРВАЦИИ на ретрае не деградирует и не оставляет следа — юнит может заклинить навсегда, а +> волна не может продолжиться без дыры.** `maxTokensForAttempt` удваивает бюджет на каждой регенерации +> (`disposition.go:398`) ⇒ attempt 1 резервирует вдвое больше attempt 0. Отказ ретрая уходит наверх из +> цикла попыток (`stagerun.go:164-167`) РАНЬШЕ `UpsertChunkStatus` (`stagerun.go:285`) ⇒ чекпойнт +> attempt 0 есть (реплей за $0), строки `chunk_status` нет, читающие модели видят `pending`. Следствия +> два: (1) если потолок покупки не вмещает attempt 1 — юнит не доделается НИКОГДА, каждый резюм реплеит +> attempt 0 за $0 и умирает на attempt 1, прогон уходит exit 4 при полностью доставленных купленных +> юнитах; (2) продолжить волну после отказа ретрая нельзя без дыры в непрерывном префиксе — то есть цена +> глобальной защёлки (недодача юнитов, которые ВЛЕЗАЛИ; разброс резервов внутри волны замерен 2.976×, +> разбор 04.09) не снимается различителем «свежий/ретрай». Два пути, ВЫБОР НЕ СДЕЛАН: (а) отказ ретрая +> деградирует во флаг attempt 0 — смена D2.3, закрывает оба следствия; (б) минимальная покупка платформы +> ≥ `step_max_usd` движка — закрывает только (1) и стоит продукту минимальной покупки ~5 глав | +> бэкенд+платформа | скоро | выбор владельца между (а) и (б) + пин | бэкенд-сессия денежного стопа 05.09 + +**ГДЕ ПРОМТ НАПИСАН НЕ ТАК** (требование §5-тер(а), полный список — §5 отчёта): §4(3) внутренне +противоречив (см. выше) · §4(1) заказывает лечение, невыполнимое без нарушения §4(6) · §3 п.1 называет +секцию `15-money-path.md`, переименованную 05.09 · §4(2) обосновывает защёлку доводом, который снят самой +конструкцией (`haltErr` — отдельный слот, путь ошибки один при любом W); защёлка оставлена по ДРУГОМУ +доводу — продуктовому · §4-тер риск (б) назван мягче, чем он есть: счётчики не «получают новую форму», а +врут по шести полям из семи.