textmachine/docs/archive/PROGRESS-2026-08-17.md

72 lines
34 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Слайс хроники: бэкенд-пак «честные числа и статусы» (17.08.2026)
> ⚠ **Архив. Инструкции отсюда НЕ исполнять, при онбординге НЕ читать** — только по конкретной ссылке.
> Вынесено оркестратором №18 22.08 из `docs/PROGRESS.md` секции «Бэкенд»: секция объявляет себя местом
> ТОЛЬКО для пингов живых сессий, а держала 60 строк отчёта закрытой работы.
>
> **Исход:** пак ИСПОЛНЕН, ПРИНЯТ С ФИКС-ЛИСТОМ и ЗАЛЕНДЕН `f840867` (17.08) — ратификация **D39.149**,
> решение владельца по ФЧ-5 — **D39.150**. ⚠ Прежний заголовок блока писал «дерево не закоммичено»;
> это было верно один день и опровергалось записью приёмки в том же блоке — испр. при выселении.
> **Живой остаток работы — строка бэклога 197** (фикс-лист ФЧ-1…ФЧ-8), её тело ссылается СЮДА.
> Состояние бэкенда — CURRENT-STATE в `../PROGRESS.md`, решения — D-лог.
### Пак «честные числа и статусы» (строки 181 · 176 · 172-г · 187) — ИСПОЛНЕН, дерево не закоммичено (17.08)
**Записка-план (пункт заказа → где).** 1) 181 — `internal/pipeline/reprice.go` (нов.) + `store/ledger.go` (`CheckpointUsageForBook`) + `rebill.go`/`status.go`; тест `reprice_test.go`. 2) 176 — `internal/obs/panic.go` (нов.), recover воркера `waverun.go:260-268`, `exitOf` в `cmd/tmctl/main.go`, ветка `*obs.PanicError`→1 в `exitCode`; usage-строка `invocation.go:96` + её замороженный тест; тесты `wavepanic_test.go`, `panic_exit_test.go`. 3) 172-г`live_reprobe_threshold_test.go` (нов., без тега) + риг `live_reprobe_test.go`. 4) 187 — `snapshot.go:179-190`, `membank/memory.go:359-365`. 5) живой прогон — исполнен, $0.005582.
**181, развилка источника чисел (решение).** Пере-прайс идёт ФАКТИЧЕСКИМИ токенами чекпоинтов через тот же шов, что и сеттл (`Pricer.PriceForResponse` + `ledger.CostUSD`), а не оценкой `EstimateUSD`. Причины: оценка резервирует весь `max_tokens` + reasoning-буфер и завысила бы кратно (лечим «число врёт вниз» числом, врущим вверх); оценке нужны отрендеренные сообщения, которых read-путь сознательно не имеет; через шов сеттла проекция не может разъехаться с тем, что забронирует леджер. Мост к usage — НЕ `FinalHash` (он только на ok-пути, как и предупреждал заказ), а джойн `checkpoints ⋈ jobs` по (chapter, chunk_idx, stage): он достаёт usage и для flagged-строк, и для всех попыток. **Обе половины гейта починены вместе:** `projectBookUSD` (база 5%-порога и `projected_book_usd`) пере-прайсится тем же репрайсером — иначе порог сравнивал бы две разные валюты. Историческими остаются `committed/reserved` и `CostUSD` глав-паспортов: потраченное — факт, а не проекция. **Не проецируется (задокументировано, как и контент-ось):** РОУТИНГ — строка прайсится моделью, которая ОТВЕТИЛА, а не той, в которую стадия резолвится сейчас. `HistoricalRows` считает строки, чей usage недостижим (чекпоинт снесён redrive'ом; billed-decode с `{}`-usage) — они несут историческую сумму, и операторский текст это НАЗЫВАЕТ.
**176, выбор кода.** Паника (любой горутины) → **1**, «infra failure and everything else». Не новый номер: обе полосы — замороженный шов, а слово в ратифицированном словаре не заводится сессией. Ветка `*obs.PanicError` стоит ПЕРВОЙ в `exitCode`. `obs.PanicError` намеренно БЕЗ `Unwrap`: паника значением `context.Canceled`/`*CeilingHalt` иначе уехала бы в 5/4 при формально нетронутом словаре (покрыто тестом на 7 значений). `obs.SafeGo` НЕ применён — он глотает. Стек едет в `Error()`, значит на stderr при любой обёртке.
**Команды и результаты.** `make battery` → EXIT=0; SKIP ровно два: `TestHelperEventsRun`, `TestHelperKillLoop`оба helper-процессы подпроцессных харнессов (`t.Skip("helper process only")` без env-ключа), не пропуск покрытия. Корпусные четыре (`TestMinerFullBookParity`, `TestCheckerLabels*`, `TestK6LabelsBaseline`) на этом стенде ПРОШЛИ, не скипнулись. Голден: `go test ./internal/pipeline/ -run Golden -count=1` → ok, бит-в-бит. `go vet -tags live ./...` → чисто. `grep -rh '^func Test' backend --include='*_test.go' | wc -l` = 819; в диффе `^-func Test` = 0 удалённых, +9 добавленных. ⚠ **Испр. приёмкой №18:** финальное число — **827** (пере-считано командой отчёта после последних правок; 819/823 — промежуточные снимки), удалённых имён 0 подтверждено.
**Красное до фикса — исполнением** (откат в рабочем дереве, без git): 181 — `re-payment projected at $0.003640 but the current price table makes that work $0.014560`; отдельно откат только базы порога — `status projected_book_usd = $0.003640, want $0.014560`. 176 — при снятом recover тест-бинарь падает паникой (`panic: worker exploded mid-chunk`), а реальный бинарь при непойманной панике выходит **2** (замерено: собранная программа-однострочник, `exit=2`). Usage-строка — `usage error text is frozen, got: usage: tmctl <translate|report|status|export|redrive|manifest> --config book.yaml`. Правок тестов ради зелени нет: изменены только `invocation_test.go` (санкция тела 176 + D39.134 п.3) и механические сайты вызова после смены сигнатур `projectRebill`/`projectBookUSD` — утверждения не тронуты.
**Пункт 5, живой прогон (санкция владельца 17.08, потолок $0.20).** Стенд `~/books/gu-zhenren/coldrun-b/reprobe/classify6/` был на схеме v14 против v15 — `status` дал ратифицированный `exit 13 schema_mismatch`, прогнан `tmctl migrate` (v14→v15, restore point взят). Замер: **4/5 прогонов достигли 6/6**, `finish=stop` на всех пяти, `bad_lines=0`, модель `deepseek-v4-flash`, `max_tokens=8000`, эффорт `unset`. **Итог: ГЕЙТ ПРОЙДЕН по ратифицированной форме — и это ровно тот 4/5, на котором старая форма «6/6 на КАЖДОМ» упала бы ложно.** Трата **$0.005582424**; леджер пробы 0.00038997→0.00597239, дельта совпала с числом рига до последнего знака, `reserved_usd=0`. Ревью исполнением: запросы прочитаны глазами (дамп `classifierMessages` в `classifier-request.json``reasoning:""`, то есть reasoning-off НЕ шлётся и thinking у DeepSeek остаётся ON, эхо-мина не взведена), сырые ответы прочитаны из `classifier-6of6-unset.json`, стоимость двух прогонов пересчитана вручную по новой таблице flash и сошлась (run0 = 1143·0.44 + 869·1.32 за 1M = $0.001650). ⚠ **Строку 172-г НЕ закрываю:** риг меряет классификатор на flash, а сменившиеся веса — у pro (редактор).
**Диспозиция строки 164 — ОСТАВЛЕНА ОТКРЫТОЙ, и она по-прежнему верна.** Сверено кодом: `Status()` (`status.go:388`) не зовёт `seedGlossary`, поэтому `r.baseMemory` на read-пути nil (`seeding.go:157-159` — единственное место присвоения), `precomputeSticky` даёт пустую инъекцию и `renderedContentHashes` не опознаёт repin (`rebill.go:166`). Пак этого не двигает: он чинит ЦЕНУ строки, а 164 — про то, какие строки вообще попадают в счёт. Разделение честнее, потому что лечение 164 — материализация банка на КАЖДОМ `status` (его платформа зовёт перед каждым спавном), то есть решение о цене read-пути, а не правка текста. Пере-прайс её не маскирует — он масштабирует завышение той же таблицей.
**Находки вне заказа (не чинил, оркестратору на завод строк).** (1) Промт §5 называет `LOG_LLM_BODIES=1`+`LOG_LEVEL=debug` носителем тел запросов для рига — **не работает**: ключ читается из `obs.ReqInfo` контекста (`cmd/tmctl/main.go:196-200` → гейт `obs/logging.go:77`), а `live_reprobe_test.go:119` строит ctx без `WithReqInfo`, поэтому тела не логируются ни при каких env. Использован второй разрешённый носитель (дамп). (2) `docs/experiments/00-provider-quirks.md` до сих пор пишет «цены DeepSeek не изменились (flash $0.14/$0.28)» — протухло со сменой 16.08 (D39.137), зона не моя. (3) Якоря строки 181 ПРОВЕРЕНЫ и оказались точными (не расхождение, называю потому что сверял): в HEAD `rebill.go:166` = `p.USD += cs.CostUSD`, `:252` = сравнение с потолком согласия, `stagerun.go:431/:613` = `EstimateUSD(PriceFor(...))` и `PriceForResponse(...)`. (4) Прежний артефакт стенда `classifier-6of6.json` — старого одно-сэмплового формата; текущий риг пишет `classifier-6of6-<effort>.json`, старый файл рядом никем не перезаписывается и вводит в заблуждение. (5) Список команд usage-строки и подсказки `unknown command` были двумя ручными копиями; сведены в один `dispatchCommands` (байты подсказки не изменились). (6) Строки 146/173/177 по пути видел — не трогал (подписанный пропуск заказа).
**Адверсариальное ревью §4 — ПРОВЕДЕНО (17.08, воркфлоу `wf_6f671334-f69`: 23 агента, 0 ошибок, 1.95M токенов, 37 мин).** Три независимые линзы (fable — деньги/алгоритм/контракт; opus — ремесло/проза/идиоматика; opus — шов exit-кодов + верность §3/§4), по скептику-опровергателю на каждую находку, критик полноты финалом. 20 находок → **11 опровергнуто**, 8 выжило, +5 от критика.
| Находка (линзы) | Вердикт | Диспозиция |
|---|---|---|
| **Репрайсер суммирует чекпоинты ВСЕХ поколений**: `checkpoints` append-only на всю жизнь книги и не несут снапшота, а `chunk_status.cost_usd` ПЕРЕЗАПИСЫВАЕТСЯ текущим поколением ⇒ после первой же пере-покупки число согласия и `projected_book_usd` завышены ×N | CONFIRMED ×3 линзы, воспроизведено исполнением | **ПРИНЯТА — регресс МОЕГО же фикса, исправлен.** Членство поколения выводится из единственного пер-поколенного авторитета в сторе — денег самой строки: обход вызовов С НОВОГО КОНЦА, пока они помещаются в `cs.CostUSD` (`reprice.go`). Потребовало порядка вставки: `ORDER BY c.rowid` (по `attempt` нельзя — он перезапускается каждый прогон) |
| **Пропущенные (`skipped`) строки прайсились по осиротевшим чекпоинтам** своей позиции — фантомные деньги в базе порога, без всякого `--resnapshot` | PARTIAL (тот же корень) | **ПРИНЯТА, закрыта тем же фиксом:** у `skipped`-строки `cs.CostUSD=0` ⇒ обход не берёт ни одного вызова |
| **Паника воркера, проигравшая гонку другой ошибке волны, исчезала бесследно**`fail` first-wins, и прогон уходил кодом сиблинга: 4 (платформа пишет `paused`) или 5 (graceful stop), стек уничтожался | PARTIAL, воспроизведено на трёх видах первой ошибки | **ПРИНЯТА, исправлена.** У краха свой слот `panicErr`, возвращается ПРЕИМУЩЕСТВЕННО перед `firstErr`; first-wins для обычных ошибок не тронут. Разошёлся со вторым скептиком, советовавшим только печатать стек: упавший процесс не «пауза» — врать в пользу возобновляемости хуже |
| **Обоснование правила остатка ложно**: докстринг ссылался на redrive, а `ResetChunkStages` сносит строку диспозиции ВМЕСТЕ с чекпоинтами в одной транзакции — сценарий недостижим | CONFIRMED / PARTIAL | **ПРИНЯТА:** остаток пере-описан честно (пол для стора, потерявшего вызовы — рестор, легаси), тест пере-написан |
| **Спека-проект `backend/docs/D15.2-*.md:398` всё ещё определяет проекцию как Σ хранимого `cost_usd`** (критик; моя зона, заказ требовал «правда или прочь») | найдено критиком | **ПРИНЯТА:** эррата в теле спеки |
| Три непокрытые мутации (цена по ОТВЕТИВШЕЙ модели · раскрытие непере-прайсенной части в тексте согласия · порядок panic-first при `errors.Join`) | найдено критиком | **ПРИНЯТЫ:** три пина добавлены; последний показан красным (перенос ветки паники ниже сентинелов даёт 2/3/10) |
| Проза: история про exit 2 пересказана в шести блоках; 33-строчная шапка `reprice.go` | ОПРОВЕРГНУТЫ по метрике (плотность файлов сдвинулась на 12 пункта, норма репо такая же) | **ЧАСТИЧНО ПРИНЯТЫ ВОПРЕКИ ОПРОВЕРЖЕНИЮ** — слово владельца о лаконичности выше довода «дом так пишет»: шапка `reprice.go` 33→14 строк. ⚠ **Испр. по раунду 2 (находка F6): моё утверждение «история про exit 2 рассказана ОДИН раз» было НЕВЕРНО** — она изложена на четырёх сайтах (`obs/panic.go`, `waverun.go`, `wavepanic_test.go`, `panic_exit_test.go`) и упомянута ещё на двух в `cmd/tmctl`. Ревьюер рекомендовал НЕ резать: каждый текст говорит про свой шов и все четыре фактически верны; исправлено утверждение, не код |
| `status` жёстко падает при ошибке загрузки репрайсера · `dispatchCommands` не выводится из switch · порядок в `CheckpointUsageForBook` избыточен · `TM_CLASSIFY6_N` мусорный игнорируется · плоская карта вместо вложенной · и ещё 6 | ОПРОВЕРГНУТЫ исполнением | Действий нет. По одной опровергнутой находке действие всё же сделал: `EXPLAIN QUERY PLAN` показал лишнюю temp-B-tree сортировку по колонкам, которых логика не использует → `ORDER BY c.rowid` |
**РАУНД 2 адверсариального ревью — ПРОВЕДЁН по дельте первого (воркфлоу `wf_bb22ee8a-a4b`: 15 агентов, 0 ошибок, 1.22M токенов, 70 мин).** Скоуп узкий и намеренный: свежий, никем не смотренный фикс. Линзы — fable по правилу членства поколения (с прямым указанием, что это ВЫВОД, а не тождество), opus по слоту `panicErr` + **мутационная проверка тестов**. 12 находок → 5 опровергнуто, 7 выжило, +3 от критика. **Вердикт критика: HAND OVER WITH NAMED CAVEATS.**
| Находка | Вердикт | Диспозиция |
|---|---|---|
| **R2-1: обход ломается на ОТКАТЕ конфига.** Правило считает, что вызовы текущего поколения — самые НОВЫЕ в ячейке. Откат промпт-конфига адресуется старым `request_hash`, сеттл становится no-op (`ON CONFLICT DO NOTHING`), строка пере-записывается стоимостью СТАРОГО вызова, а новый остаётся на файле. Воспроизведено сквозным прогоном: тихое ×2 завышение при `HistoricalRows=0` | PARTIAL major. ⚠ Скептик снял главное обвинение: **это НЕ регресс моего фикса** — до-фиксный код даёт то же число в этом потоке; направление «занижение» ново, но показано только синтетически | **ПРИНЯТА.** Добавлен детектор: перебор `accounted > cs.CostUSD` **доказывает**, что новейшие вызовы не принадлежат строке ⇒ откат к сумме счёта с `fromHistory=true`. Тихое ×2 меняется на устаревшую цену, названную вслух. Точное решение требует маркера поколения на `checkpoints` (схема денежной таблицы) — **вопрос на ратификацию, а не третья заплата** |
| **F1 (blocker): направление обхода НЕ ПРИБИТО** — переворот на «от старых» переживал всю зелень: мой фикстур был симметричен (два одинаковых поколения) | PARTIAL | **ПРИНЯТА:** поколения сделаны асимметричными ($0.60 против $0.05); мутация теперь убита |
| **F2: `ORDER BY c.rowid` не прибит** — и `ORDER BY c.attempt`, и полное удаление переживали зелень. Плюс: планировщик показывает, что клауза temp-B-tree не убирает, а ДОБАВЛЯЕТ | PARTIAL major | **ПРИНЯТА:** store-тест с немонотонными `attempt` (0,1,0,1 против порядка вставки). ⚠ Моё обоснование через `EXPLAIN QUERY PLAN` в записи выше было неверно: клауза нужна для КОРРЕКТНОСТИ, а не ради плана |
| **F4: счётчик раскрытия `HistoricalRows` не прибит** — удаление инкремента переживало зелень | PARTIAL minor | **ПРИНЯТА:** тест ведёт флаг из `rp.usd` через реальную `projectRebill` до текста согласия |
| **F8/R2-2: докстринг guard-теста называл формы, которых фикстуры не строят** (ретрай, производные экспорты) — дрейф док-против-кода в паке про честность | PARTIAL | **ПРИНЯТА:** комментарий говорит ровно то, что тест гоняет |
| **C2 (критик): текст согласия звал ЧАСТИЧНО пере-прайсенную единицу целиком исторической** («their usage is no longer on file») | найдено критиком | **ПРИНЯТА:** формулировка исправлена |
| **C1 (критик): `dispatchCommands` прибит не в ту сторону** — выброс команды ИЗ списка оставлял зелень | найдено критиком | **ПРИНЯТА:** AST-тест читает сам switch `run()` и сверяет со списком в обе стороны |
| **C3 (критик): мусорный `TM_CLASSIFY6_N` тихо игнорировался** и проба покупала 5 вызовов — прямо над громким гейтом | найдено критиком | **ПРИНЯТА:** нечисловой/неположительный — громкий отказ до первого вызова |
| F6 (проза), и 5 опровергнутых: $0-строка не пере-прайсится вверх · $0-вызов теряется · при победе паники теряется ошибка сиблинга · `Status` жёстко падает · «крах не может уйти кодом 2» неверно для `fatal error` | ОПРОВЕРГНУТЫ | Кода не трогал. Последнее — честное уточнение: неперехватываемые `fatal error` (конкурентная запись в map, OOM) по-прежнему дают 2; `recover` их не ловит по устройству Go. **Назвать оркестратору как остаточный класс** |
**Мутационная приёмка исполнением.** Четыре мутации, которые у ревьюера ПЕРЕЖИЛИ зелень, теперь убиты: обход от старых → `TestRepricerCountsOnlyTheCurrentGenerationOfCheckpoints`; `ORDER BY c.attempt``TestCheckpointUsageForBookReturnsInsertionOrder`; снятие `HistoricalRows++``TestProjectionCountsRowsItCouldNotRePrice`; снятие детектора перебора → тот же генерационный тест.
**Что критик проверил САМ и подтвердил** (не мои клеймы, его прогоны): батарея EXIT=0 с ровно двумя SKIP; голден сверяется с ФАЙЛОМ `testdata/golden/capture.golden`, не сам с собой, и файл не в diff; wire-нейтральность доказана трижды (голден · оба хеш-несущих файла в дельте изменены ТОЛЬКО в комментариях · под `prompts/`/`configs/`/`langpacks/`/`testdata/` нет ни одного изменения); ноль удалённых имён тестов; оба регресса раунда 1 красные на до-фиксном коде; ни `platform/`, ни `frontend/`, ни контракт 14 не читают `projected_book_usd`/`rebill_usd`; **новое чтение замерено на синтетическом сторе 800 глав / 4800 чекпоинтов / 64 МБ — 3943 мс** против 10-секундного бюджета операции. Мой счёт тестов был на единицу меньше фактического.
**Перепроверка после ревью — командами.** `make battery` → EXIT=0, те же два SKIP. Голден → ok. `go vet -tags live ./...` → чисто. `^func Test` = 823 (было 819), удалённых имён 0. Красное до фикса показано исполнением для ОБОИХ регрессов: `projected $0.007280` против настоящих `$0.003640` (пере-купленная книга) и `wave returned *pipeline.CeilingHalt` / `*errors.errorString: context canceled` вместо паники. Замер стоимости нового чтения: на самом большом реальном проекте (coldrun-a) 149 чекпоинтов, план — два индексных поиска, 2.13 мс.
**Obstacle.** (а) Интервальная самопроверка субагентом в СЕРЕДИНЕ работы не проводилась — ревью запускалось финалом по слову владельца; оба регресса нашлись бы раньше, промежуточный прогон линз стоил бы дешевле. (в) Живой прогон стартовал ~00:58 UTC и захватил границу пикового окна 0104 UTC (правило-рекомендация «не стартовать в пик»): при трате $0.0056 против потолка $0.20 ожидание трёх часов было бы несоразмерно, но факт называю. (г) `git status` показывает чужой ИНДЕКС (полигон: `eval/`, `docs/experiments/`) — не трогал и не прибирал; своё не стейджил.
**Запись оркестратора №18, 17.08 — ПРИЁМКА ПАКА «ЧЕСТНЫЕ ЧИСЛА И СТАТУСЫ»: ПРИНЯТ С ФИКС-ЛИСТОМ, залендено.** Панель 5 линз (слепая по заказу+диффу ДО отчёта · деньги · шов · вне карты · собственные мутации в копии) + пере-раны оркестратора. **Своей рукой пере-проверено исполнением:** `make battery` EXIT=0, SKIP ровно два и оба helper-процессы · голден `-run Golden` ok · `^func Test` = **827**, удалённых имён 0 · **деньги пробы ДВУМЯ независимыми путями — сумма чекпоинтов минус вызов 02.08 и пере-счёт из сырого `usage_json` по запинённой таблице `models.yaml` — оба дали $0.005582424**, до последнего знака · вердикт гейта из сырого артефакта: `runs: 5, runs_at_threshold: 4`, `finish=stop` ×5 ⇒ PASS по ратифицированной форме и ложное падение по прежней. Регрессов против HEAD панель не нашла: три места, где движок врал, действительно перестали врать.
**Фикс-лист ФЧ-1…ФЧ-8** (ни один не блокирует лендинг; все — «заявленное не прибито» либо «текст обещает больше числа»). **ФЧ-1 (HIGH, три неприбитые гарантии, мутации ПЕРЕЖИЛИ зелень):** снятие фильтра по книге в `CheckpointUsageForBook` (`store/ledger.go:371`) не валит ни store-, ни денежные pipeline-тесты — **воспроизведено оркестратором лично**, а это единственная защита от того, чтобы деньги ЧУЖОЙ книги вошли в число согласия · ключ ячейки можно лишить `chunk_idx`, и обход начнёт съедать деньги соседнего чанка (во всех фикстурах глава = один чанк) · ветка пометки «строка не пере-оценена» не ловится на продовом пути. **ФЧ-2 (MED):** два докстринга `reprice.go` описывают НЕ то, что делает код (детекция перебора односторонняя — суффикс вытесненных вызовов с точной суммой проходит молча; «остаток несётся по счёту» — код сперва съедает вызовы прошлых поколений) — в паке про честность это тот же класс, что чинился. **ФЧ-3 (MED):** заказ 187 исполнен наполовину — снятый закон approved-only продолжает утверждаться в докстринге теста и в `backend/docs/D15.2-*.md:317`. **ФЧ-4 (MED):** раскрытие «часть суммы — старые деньги» не доезжает до `status`/`--json` (`HistoricalRows` теряется на границе `status.go`), а второй операторский текст отказа (`--accept-rebill`) цитирует пере-прайсенное число без basis. **ФЧ-5 (MED, деньги — решение владельца):** пере-прайс чинит ось ЦЕНЫ, но не ось МОДЕЛИ — при смене модели стадии (модель входит в снапшот) число считается ценой ОТСТАВЛЕННОЙ модели, замер линзы: $0.003640 против честных $0.036400 при `HistoricalRows=0`, и фраза согласия при этом заявляет «current price table» без оговорки. Не регресс, но обещание §0 промта для этого триггера остаётся живым. **ФЧ-6 (LOW):** `Status()` жёстко падает при ошибке репрайсера — в трёх строках над сознательной политикой «ошибка проекции репортится, но отчёт не валит»; это канал сеттла платформы. **ФЧ-7 (MED, шов):** паника воркера ПОВЕРХ пойманного потолка уничтожает факт потолка на обоих каналах — прогон, остановленный ceiling'ом, платформа запишет `failed`, что PD-113 прямо запрещает (не регресс; до пака терялось иначе). **ФЧ-8 (LOW):** докстринг `TranslateBook` про «каждый выход через `terminal()`» неверен для паники вне волны · комментарий `dispatchCommands` («список теперь один») — usage-строка осталась второй копией · шапка рига ссылается на заменённую ратификацию.
**Подтверждено панелью как ВЕРНОЕ (клеймы сессии, пере-проверенные независимо):** словарь кодов побайтно не двинут, добавлен ровно один `return 1`; «паника → 1» читается платформой как `failed` во всех пяти точках потребления (инвентарь: `ingest/exit.go` · `ingest/supervisor.go` · `runner/engine.go` · `runner/marker.go` · `runs/reconcile.go` · `books/parse.go`), тогда как ДО пака упавший посреди книги прогон записывался `ready` · остаточный класс подтверждён: непойманные `fatal error` (конкурентная запись в map, OOM) по-прежнему дают 2, `recover` их не ловит · wire-нейтральность: оба хеш-несущих файла изменены ТОЛЬКО комментариями · строка 164 после пака по-прежнему верна · клейм сессии про банковые роли верен И это преexisting — заведена строка **194**.
**Что НЕ проверено:** мутации ФЧ-1 (2) и (3) приняты со слов мутационной линзы — лично воспроизведён только фильтр книги · старый вызов 02.08 в леджере пробы не сошёлся ни с одной из двух ценовых таблиц (вне скоупа пробы, не разбирался) · перф нового чтения на боевой книге — замер только синтетический и со слов сессии.