# Журнал зоны «Платформа» > Весь прогресс платформы — ЗДЕСЬ (решение владельца 04.08): пинги, итоги сессий, открытые > вопросы, предложения на ратификацию. В `docs/PROGRESS.md` платформа не пишет; оркестратор > читает этот журнал при каждом лендинге зоны (свип «решений владельца» — норма D39.99 п.4). ## ПЕРЕСБОР P10 ПО ДИСПОЗИЦИЯМ ИСПОЛНЕН — форма БЕЗ сметы, все принятые корни закрыты, посадки 4/4 (сессия платформы, 28.08, после самопрохода ниже) Диспозиции оркестратора (эррата 28.08-к; пересборка принята целиком; §3.2 — «до твоего холда»; полоса — «одна единица работы»; resume — «купи заново»; якоря journal-доков не чинить до лендинга). ### Что легло (карта корней → лечение) - **K1/K2/K5/K10/K11-острота — умерли вместе со сметой.** `recordBankMove`(со status), `rebillConsent`, `rebillCentGuard`, `ErrRebillOutgrown`, сметные колонки books — УПРАЗДНЕНЫ; миграция 00027 пересобрана (только `bank_moved_at` + консенты строки прогона), sha256 пере-подписан. Факт — от СВОЕЙ квитанции: `corrected(rec)` = `changed:true` ЛИБО хоть один `already_applied` (ретрай-сходимость держится состояниями квитанции; провал записи → `bank_corrections_incomplete`, ре-сенд дописывает). `tmctl status` НЕ зовётся ни в двери, ни в Start, ни в Resume — «status — ремонт, не поллинг» восстановлен, ошибки движка больше не валят допуск, пиннинг-вопрос (K9-бинарь) беспредметен. - **K6/K8/часы — умерли вместе с временны́м предикатом.** Предикат факта = `bank_moved_at is not null`; гашение ЯВНОЕ и только успехом: `reconcile.finish` при `l.Resnapshot && status=="ready"` → `ClearBankMove` (вне транзакции finish НАМЕРЕННО: асимметрия «застрявший факт = один безвредный `--resnapshot`; ложно-снятый = смерть на гарде» — комментарий в коде). failed/stopped/paused оставляют факт → петля K6 разорвана; правка в окне `awaiting_bank` видна resume того же прогона (K8) — лишний флаг безвреден по построению гарда (читается только при реальном сдвиге снапшота). - **K7 — консент ФОНДИРОВАН**: `--accept-rebill=<холд ЭТОГО прогона>` (обычная покупка — `Ceiling(C)`; resume — полный бюджет строки; re-pass — свой холд). Бланкет-оговорка (K11) сужена до честной: кап не «различает» источники дрейфа — он ограничивает трату деньгами, которые пользователь дал. - **K4 — resume re-pass закрыт словом**: `ErrNotResumable` «a re-pass is bought again» ДО reopen-арифметики (Ceiling(0)-ловушка недостижима); прерванный re-pass оставляет факт → повторная покупка доступна (запинено). - **§3.2 «до твоего холда»**: холд re-pass = `Ceiling(chapter_count)` — честный потолок «вплоть до полного пере-перевода», незатронутое $0, разница released; гейт — только факт (`BankMoved`); `BookRunContext` вырос полем `ChapterCount`. - **K3 — полоса «одна единица работы»**: `runDone` C=0 → 0, и 1 при `finished_at∧ready`; `runTotal` C=0 → литерал 1 (0/0 недостижим; `chapter_count`-мутабельность total'а умерла); `stage='re_pass'`. Канонная оговорка — за оркестратором. - **K12-live**: живой тест теперь гоняет ОБА флага против настоящего движка (`--accept-rebill=0.030000` в проходной половине — движковый гейт принял). - `Options.RebillUnits` снят (показ «N юнитов» отложен вместе со сметой — строка бэклога оркестратора на движковый глагол). ### Пины и посадки пересбора Переписаны/добавлены: `TestACorrectionRecordsTheBankMoveAndAPreviewDoesNot` (+ветка already_applied пере-штампует после ClearBankMove) · `TestAStartOverAMovedBankCarriesBothConsentsToTheSpawn` (`--accept-rebill=0.060000` = холд 2 глав) · `TestAFailedRunKeepsTheFactAndAReadyRunRetiresIt` (жизненный цикл факта через настоящий Sweep-путь) · `TestTheRePassDoorAdmitsOnAMovedBankAndRefusesWithoutOne` (холд −150000 = вся 5-главная книга) · `TestAResumeOverAMovedBankGrantsTheConsents` (90000 = бюджет строки) · `TestARePassIsBoughtAgainNotResumed` · `TestARePassRunsBarIsOneUnitOfWork` (0/1 → 1/1; C=0 без консента отвергнут). Посадки: M-B2 (критерий квитанции мёртв) · M-C2 (гасит любой исход) · M-G (консент не фондирован) · M-H (ветка полосы снята → 0/0 пойман) — **4/4 топично**. ### Числа пересбора (командой) `go test ./... -race -count=1 -v` с гейтами → **EXIT=0, 18 пакетов ok, SKIP=0, RUN=803**, FAIL/DATA RACE — 0; `golangci-lint` → **0 issues**; `gofmt -l` пусто. Опись: `git status --short -- platform/` — дифф СЖАЛСЯ против сданного (упразднений больше, чем добавлений). ### Дописка: провод 0.7.0 смонтирован (четыре пункта приёмки) 1. `ContractVersion` → **0.7.0**, гейт версии зелёный. 2. **`re_pass` на проводе**: `wireRunRequest.re_pass`; `ceiling_chapters` обязателен только для обычной покупки; оба вместе → 400 malformed (канонное взаимоисключение); `StartRequest` собирается по форме. Пин `TestARePassRequestIsItsOwnPurchaseShape` (три стороны: доходит до сервиса как re-pass · оба вместе 400 · «нечего» → 409 со своим cause). 3. **`cause.code: re_pass_unavailable`** — свой код взамен временного `bounds_moved` (`CauseRePassUnavailable`, маппинг `ErrRePassUnavailable`). 4. **`rebill_units`/`rebill_usd` СНЯТЫ из аллоулиста шва** (слово приёмки: поле без потребителя — класс, который пак лечит; основание взятия снято эрратой 28.08-к) — вместе с декод-пином; в шапке `StatusReport` осталось ИМЕНОВАННОЕ объяснение, почему пара не берётся (тайминг свёртки) и с чем вернётся (движковый глагол сметы). Батарея после провода (финальная этого раунда): `go test ./... -race -count=1 -v` с гейтами → **EXIT=0, 18 пакетов ok, SKIP=0, RUN=803**, FAIL/DATA RACE — 0; линт **0 issues**; `gofmt -l` пусто. ### Остатки, названные честно - ~~`rebill_units`/`rebill_usd` остаются в аллоулисте~~ — СНЯТО приёмкой (см. дописку выше): пара убрана из шва целиком до движкового глагола сметы. - Правки банка, сделанные в ОДНИ СУТКИ жизни P9-двери ДО деплоя P10, факта не имеют (миграционный in-flight): их продолжение может поймать гард; лечение — повторный apply того же документа после деплоя (byte no-op проставит факт по already_applied-ветке). - Консент-гейт движка живьём деньгами по-прежнему не пробит ($0-цены; кандидат строки 202) — из прежнего Obstacle, не изменилось. ## ⚠ ШИРОКИЙ САМОПРОХОД P10 (заказ оркестратора 28.08): ПАК В СДАННОЙ ФОРМЕ НЕСОСТОЯТЕЛЕН — 42 находки/6 линз, три корня валят ФОРМУ; диспозиция и СТОП до слова оркестратора (сессия платформы, 28.08) Заказ «найди, где автор неправ» исполнен воркфлоу (6 линз: 1×Fable на деньги + 3×opus + 2×sonnet, 42 находки, 57 not_refuted; полные траектории — журнал wf_323e81c4-3e3). **Запись ниже — по норме «дефекты, внесённые самим паком, первыми»; сданная выше запись «ПАК P10 ОТРАБОТАН» в части §3.1-мины и §3.2-полосы ОПРОВЕРГНУТА этим проходом.** По дереву НИЧЕГО не менено после сдачи — пересбор по диспозиции ниже требует слова оркестратора (два корня пересматривают его решения: D39.165-предпосылку и главу-форму полосы). ### Корни (дедуплицировано из 42; K1-K3 — фатальные для формы) | # | Корень | Улика | |---|---|---| | K1 | **СЛЕПОЕ ОКНО: смета в момент правки НЕ СУЩЕСТВУЕТ.** `tmctl status` считает дрифт/ре-билл от stored memory, а `bank-apply` пишет только файлы решений — свёртка происходит ВНУТРИ следующего translate. Сразу после apply живой движок отдаёт `rebill_units=0, config_drift=false` (исполнено агентами на настоящем tmctl, дважды подряд) ⇒ `recordBankMove` не пишет НИЧЕГО, факт не взводится, флаги не выдаются, **мина стоит** — а мой live-тест проверял движковый гард НАПРЯМУЮ (TranslateArgs руками) и платформенную цепь не покрывал: класс M-F («фейк мимо шва») в центре моего же §3.1, `fakeEngine.report={RebillUnits:3}` — проекция, которой настоящий движок в этом окне не отдаёт. Рушится ВСЁ на смете: материализация · сравнение живой/мат · продажа «затронуто N юнитов» · `rebillConsent`. ⚠ Предпосылка D39.165 §3 «смета уже публикуется в status --json» верна только ПОСЛЕ свёртки — для двери она недостижима без нового движкового глагола/флага (свёртка вне translate) | `bank.go:308-320` vs `backend/internal/pipeline/status.go:634-660`; живое исполнение 3 линз независимо | | K2 | **$0-цена мурует дверь**: `rebill_usd` у движка `float64,omitempty` — честные «units>0 по $0.00» приходят как units>0 БЕЗ цены; мой отказ «no price» → вечный `ErrBankIncomplete`, ре-сенд не сходится. Все $0/локальные деплои | `bank.go:316-320` vs `status.go:208-209`; 4 линзы | | K3 | **Полоса-глава пере-прохода МЕРТВА**: движок анонсирует юнит ОДИН РАЗ НА ЖИЗНЬ КНИГИ (announce-once ledger, ключ без снапшота) — пере-проход не ре-анонсирует ни репины, ни пере-переводы, `unit_resolutions.at` не двигается, done=0 навсегда (и `runs.draft_done`-канал тоже молчит). ⚠ Предпосылка глава-формы («каждый визит ре-резолвит юниты») опровергнута первоисточником — решение оркестратора требует пересмотра с этой уликой | `readmodel.go:481` vs `backend/internal/store/outbox.go:58-74`, `events.go:331-336`; 2 линзы | | K4 | Пере-проход не переживает прерываний И запечатывает дверь: reopen budget=`Pricing.Ceiling(0)`=0 → `ceilingSpent` → resume 409 `ceiling_reached`, а факт погашен `finished_at` мёртвого прогона → RePass «nothing to re-pass». Ребут → `paused/credit_exhausted` (лживое слово) через ветку «Unreachable today» | `reconcile.go:1094-1102`; исполнено тестом агента (PASS) | | K5 | «Холд строго положителен по построению» — ЛОЖЬ: гейт RePass судит МАТЕРИАЛИЗОВАННЫЕ units, холд — ЖИВОЙ consent; живой 0 при мат>0 → «hold must be positive» → **500 internal_error** (исполнено) | `runs.go:313` vs `bank.go:345-348` | | K6 | Факт гасится ЛЮБЫМ `finished_at` — включая failed-прогон, умерший на гарде с $0: **вечная петля** Start-без-флагов→гард→failed→… (исполнено агентом). Формулировка отчёта «успешный финиш гасит» не соответствовала коду | `books.go:1008-1012` | | K7 | Консент не фондирован на обычной покупке: `--accept-rebill=5.01` при `--ceiling-usd 0.03` — прогон обязан сжечь бюджет на ре-билл и встать на потолке; и частичный ре-пин при этом гасит факт | `runs.go:295-306` vs `rebill.go:292-301` | | K8 | Правка в окне `awaiting_bank`: факт невидим для resume того же прогона (AND not-exists-live) и потом гасится его же finished_at — окно, которое P9 открывал, P10 не обслуживает | `books.go:1008-1011` | | K9 | `rebillConsent` в Resume читает ТЕКУЩИЙ `Cfg.EngineBinary`, не пиннутый `l.EngineBinary` — против дисциплины строки 139 своей же зоны | `bank.go:340` vs `spawn.go:245-254` | | K10 | Ошибка движкового status в Start/Resume валит допуск ЦЕЛИКОМ словом 500; status (`projectRebill`→`withText`: полный ре-чанк+хеш-рендер книги) стоит ДО bounds-проверки и под мьютексом — «status — ремонт, не поллинг» нарушен трижды | `runs.go:300-306`, `reconcile.go:1119-1131` | | K11 | Мой «капнутый консент» — бланкет в кепке: derived FROM the projection he bounds; мат. смета сама включает ДО-правочный деплой-дрейф → ⛔-различение работает только на дрейф ПОСЛЕ правки | `bank.go:355` vs `rebill.go:292-302` | | K12 | Россыпь: два часовых источника предиката (now() БД vs s.now()) · `chapter_count` мутабелен в total (против канона «total = покупка») · комментарий «flagship = resume паузы» называет случай, который код отвергает (`paused`→`ceiling_reached`) · D39.165-половина «показывает N юнитов» не доставлена (Options.RebillUnits без провода — и без сметы недоставима) · миграционный in-flight без консентов · live-тест не гоняет `--accept-rebill` живьём (acceptRebill=0 во всех трёх вызовах — имя теста шире правды) | таблица находок, журнал wf | ### Диспозиция (МОЁ предложение; исполняю ПОСЛЕ слова оркестратора) **Чинить своё пересбором на форму БЕЗ сметы (закрывает K1,K2,K5,K6,K7,K8,K9,K10,K11 разом):** 1. Факт «банк двигался» — от СВОЕЙ квитанции, не от движка: взвод при apply с `changed==true` ЛИБО хоть одним `already_applied` (ретрай-сходимость держится состояниями квитанции, status не нужен); `recordBankMove`/`rebillConsent`/`ErrRebillOutgrown`/сметные колонки — УПРАЗДНИТЬ (миграция 00027 пересобирается: только `bank_moved_at` + консенты строки). 2. Гашение факта — ЯВНОЕ, не временнОе: `finish` при `l.Resnapshot && outcome==ready` → сброс (K6-петля умирает; K8-окно работает — «лишний» `--resnapshot` при недвинутом снапшоте безвреден по построению гарда: читается только при несовпадении снапшота; часовой dispute умирает вместе с предикатом времени). 3. Консент — ФОНДИРОВАННЫЙ: `--accept-rebill=<холд ЭТОГО прогона>` (Pricing.Ceiling(C)) — «согласен пере-платить не больше, чем этот прогон вообще может потратить»; удовлетворяет ⛔-«либо согласие явным» деньгами, которые пользователь уже дал; K7 умирает (кап=бюджет), K11 сужается до честной оговорки. 4. K4: resume пере-прохода закрыть честным словом (`ErrNotResumable`: «пере-проход не резюмится — купи заново»; факт при нефинальном исходе стоит по п.2 → пере-покупка доступна). **СТОП — решения оркестратора (не чиню):** 5. **§3.2-продажа** «затронуто N юнитов + холд от сметы» в текущем шве НЕДОСТИЖИМА (K1): либо движковый глагол/флаг «свернуть и оценить» (пинг бэкенду, их пак жив), либо продажа вслепую с иным холдом, либо §3.2 откладывается. D39.165-предпосылка требует эрраты. 6. **Полоса пере-прохода** (K3): глава-форма мертва первоисточником; варианты — движок ре-анонсирует при ре-резолве (глагольная половина) / полоса «одна работа» (0→1 на финише) / без полосы. Твоё слово. 7. Канонные хвосты §3.4 — как решишь по 5-6. Дерево не менялось после сдачи; жду слова. Дерево передаётся на лендинг (правки P10 поверх заленженного P9). Вопрос формы §3.2 решён оркестратором в ходе пака (глава-полоса, `re_pass`-форма запроса — его слово в канале). ### Таблица комплектности против §3 (пункт → сделано → каким ИСПОЛНЕНИЕМ подтверждено) | §3 | Что сделано | Исполнение | |---|---|---| | §3.1 мина | Носитель факта: миграция `00027` (`books.bank_moved_at` + материализованная смета `rebill_units`/`rebill_usd_micro`; `runs.resnapshot`/`accept_rebill_micro`); предикат факта — `bank_moved_at > max(finished_at)` книги, ОДНО сравнение закрывает все три края промта (превью/no-op не пишут — запись от ПРОЕКЦИИ, не от `changed`, и потому ретрай после провала записи сходится; правка в окне `awaiting_bank` даёт проекцию 0 — её волна без джобов — и факта не оставляет; успешный финиш нового прогона гасит факт сам). Дверь правок после каждого apply снимает `tmctl status` под тем же `lockBook` и пишет факт+смету (`recordBankMove`); провал записи → `bank_corrections_incomplete` (ре-сенд сходится байтовым no-op). Start/Resume решают ОБА флага один раз под мьютексом → строка прогона → `TranslateArgs` рендерит `--resnapshot` и ВСЕГДА КАПНУТЫЙ `--accept-rebill=<материализованная сумма + цент float-запаса>` (никогда bare-форму); реконсилерские рестарты флаги не пере-выводят (argv стабилен — дисциплина `verify_bank`). **Различение банкового сдвига от деплойного (⛔-пункт)** — сравнением ЖИВОЙ проекции с материализованной: материализованная снята В МОМЕНТ правки (цена банкового сдвига до всякого дрейфа), рост сверх неё+цент → `ErrRebillOutgrown` → 409 ДО холда; движковый гейт «потолок ниже проекции — отказ» остаётся вторым рубежом | **ЖИВОЕ РЕПРО на настоящем движке** `TestTheSnapshotGuardIsLoudWithoutTheFlagsAndPassesWithThem` (runner, гейтед): полный прайм-прогон обеих волн на $0-заглушке (порт local-провайдера, in-test) → живой `bank-apply` двигает банк → **без флага гард стреляет громко и НАЗЫВАЕТ `--resnapshot`** (состояние эрраты 28.08-и ДОКАЗАНО этим же отказом — зелёный здесь = вырожденная фикстура = Fatal) → с флагом проходит ($0-репин + пере-перевод затронутых). Пины: `TestACorrectionRecordsTheBankMoveAndAPreviewDoesNot` · `TestAStartOverAMovedBankCarriesBothConsentsToTheSpawn` (argv из спавна: `--accept-rebill=0.130000`) · `TestAResumeOverAMovedBankGrantsTheConsents` · `TestAGrownProjectionRefusesBeforeTheHold` (runs не вырос, холд не взят) · `TestTheConsentFlagsRenderExactlyWhenGranted` · `TestTheRebillProjectionCrossesTheSeam` (декод сырого JSON). Посадки M-A..M-F — 6/6 пойманы | | §3.2 дверь | `StartRequest.RePass`: на книге с фактом и `RebillUnits>0` допускается прогон с `ceiling_chapters=0`; **холд = согласованная смета + цент** (центы, не главы×$0.03; строго положителен по построению — `CeilingTemplate` не откажет, `bookCap` остаётся положительным приращением); без факта — `ErrRePassUnavailable` (временно 409 `bounds_moved`; свой cause — в §3.4). **Полоса пере-прохода — ГЛАВА-форма по слову оркестратора**: `done` = distinct-главы, которых пере-проход коснулся (`unit_resolutions.at >= r.started_at` — движковые event-времена), `total` = `chapter_count`, `stage='re_pass'` (открытый словарь D39.163); инвариант «0/0 недостижим» держится глава-формой + пол `greatest(chapter_count,1)`; `C=0` без консента по-прежнему отвергается | `TestTheRePassDoorAdmitsOnAMovedBankAndRefusesWithoutOne` (обе стороны; холд −130000 в леджере, C=0 в строке) · `TestARePassRunsBarWalksTheBook` (0/2 `re_pass` на старте → 1/2 после касания главы; C=0 без консента отвергнут) | | §3.3 смета | `rebill_units`+`rebill_usd` в аллоулисте `StatusReport` (ОБА, по поправке промта; доллары → micro-USD НА ШВЕ как `Spend`, указатель — absent ≠ бесплатно; units>0 без цены = отказ шва, не нулевое согласие); `runs.Options.RebillUnits` — юниты для показа до покупки (провод — контрактная половина); наружу доллары НЕ выходят | декод-пин `TestTheRebillProjectionCrossesTheSeam`; опровергатель на пропуск СПАВНЕН с мандатом промта (§4.5) — вердикт аддендумом ниже | | §3.4 контракт | ОПИСАНО предложением ниже, канон не тронут | секция «Предложение §3.4» ниже | ### Числа (каждое — командой) - Батарея с гейтами: `go test ./... -race -count=1 -v` → **EXIT=0, 18 пакетов ok, `grep -c -- '--- SKIP'` → 0, `grep -c '^=== RUN'` → 802**, FAIL/DATA RACE — 0. ⚠ Финальный прогон после последней правки (Options.RebillUnits) — дописка ниже. - **Скипы БЕЗ DSN (⚠-требование §4.1, командой):** `env -u TM_PLATFORM_TEST_DSN … go test ./...` → EXIT=0, `grep -c -- '--- SKIP'` → **286** (из 783 RUN) — «зелень без DSN» не проверяет треть зоны запусками и ещё часть скипами в TestMain; ловушка D39.162 воспроизведена числом. - Линтер `golangci-lint run` → **0 issues** (4 находки — noctx×2/staticcheck/gofmt в новом live-тесте — починены); `gofmt -l` пусто. - Посадки: **M-A** argv теряет `--resnapshot` → `TestTheConsentFlags` · **M-B** дверь не пишет факт → `TestACorrectionRecords` · **M-C** сравнение смет снято → `TestAGrownProjection` · **M-D** RePass без факта → `TestTheRePassDoor` · **M-E** глава-ветка полосы снята → `TestARePassRunsBar` (0/0 пойман) · **M-F** json-тег сметы сломан → `TestTheRebillProjectionCrossesTheSeam`. **6/6 пойманы топично** (копия `~/tm-p9-mut2`, база зелёная). ⚠ Урок M-F честно: ПЕРВАЯ посадка прошла все сервис-тесты — `fakeEngine` отдаёт Go-структуру МИМО json-тегов; ловец — только пин на декоде сырого JSON. Класс «фейк ходит мимо шва» — знать при ревью любых шов-полей. ### Предложение §3.4 (контрактная половина — правит оркестратор) 1. **`RunOptions`**: поле `re_pass_units` (int, ≥0; 0 = пере-прохода нет) — «затронуто N юнитов»; имя с `re_pass`, не «rebill» (движковое слово на проводе не живёт). Внутренний носитель готов (`runs.Options.RebillUnits`). 2. **`POST /runs`**: булев член `re_pass` (по твоей ноте-решению; без `ceiling_chapters`), легален только при `re_pass_units > 0` в опциях; ответ — обычный Run с полосой глава-формы. 3. **`Progress`**: оговорка к `total` («what this run bought» → для пере-прохода «работа, которую прогон обходит» — твоя формулировка в канале); `stage` получает значение `re_pass` в примерах открытого словаря. 4. **`openapi.yaml:590`** «finished work is not bought twice» → оговорка: пере-проход покупает НЕ работу заново, а доставку правки в уже купленное; цена — только затронутые юниты, незатронутое $0 (D39.165 §3). 5. **§2.12 компаньона**: «ре-билл» остаётся запретным СЛОВОМ провода, но исключение для СЧЁТА работы: `re_pass_units` — счёт той же природы, что `total_units`; доллары проекции запретными остаются (D39.84). 6. **Cause-коды** взамен временного `bounds_moved`: `re_pass_outgrown` (живая проекция выросла сверх показанной — пере-читай опции) и `re_pass_unavailable` (пере-прохода нет — банк не двигался или не тронул оплаченного). Оба 409 на `startRun`. ### Аддендум: вердикт опровергателя §3.3 (заказ §4.5) и финальная батарея Опровергатель (мандат промта: «покажи, где пропуск нарушает §2.12 или D39.84») вернулся с разбором по трём основаниям и полной трассой значений: - **§2.12 — НЕ нарушает**, и довод сильнее моего: «пять денежных полей» §2.12 — поимённо `RebillUSD`/`CommittedUSD`/`ReservedUSD`/`BookCeilingUSD`/`ProjectedBookUSD`, и ДВА из пяти (`Spend`, `Reserved`) уже ЛЕГАЛЬНО пересекают шов в заленженном аллоулисте — чтение «§2.12 запрещает шов» делало бы их нарушениями задним числом; `rebill_usd` — третье из пяти. D39.165 §3 допускает оба поля поимённо и пофайльно. Правка §2.12 остаётся ОБЯЗАТЕЛЬНОЙ (безусловная формулировка учит обратному) — уже в предложении §3.4. ⚠ Плюс его находка ДЛЯ ОРКЕСТРАТОРА: якоря §2.12 (`pipeline/status.go:58-130`, `:37-55`) ПРОТУХЛИ — волновая машинерия D39.122 сдвинула структуры (деньги теперь в `ChapterPassport`:153-175 и `StatusReport`:178-269). - **D39.84 — не нарушает** (запрет — поля в UI; сам D39.84 называет «rebill-согласие движка» как нетронутую легитимную механику; доля-не-сумма проверена на `wireUsage`). - **Шапка `resync.go:10-13` НАРУШАЛА — моя вина, поймано им, починено этим же деревом:** список «absent on purpose» всё ещё называл `rebill` и формулировал правило как «must not cross the seam» — два чтения в одном док-комментарии. Вычеркнуто, различение шов/провод внесено в шапку с именем находки. - **Путей утечки на провод/в лог НЕ найдено** — полная трасса обоих значений (БД → строка прогона → argv [не логируется по PD-99] → движок; синк ре-синка ре-билл-поля НЕ материализует; options и projectRun денег не несут; INFO-строки несут только id и счёт глав; метрики/SSE чисты). - Попутные его факты: (а) `re_pass` в `stage` легален (открытый словарь), но канону значение записать — уже п.3 предложения §3.4; (б) проводного носителя `re_pass` у двери нет — ВЕРНО, это и есть контрактная половина (п.2 предложения): платформенная половина пака сознательно недостижима с провода до канонного члена. **Финальная батарея** (после ВСЕХ правок, включая Options.RebillUnits и шапку resync): `go test ./... -race -count=1 -v` с тремя гейтами → **EXIT=0, 18 пакетов ok, SKIP=0, RUN=802**, FAIL/DATA RACE — 0; линтер **0 issues**; `gofmt -l` пусто. ### Obstacle — что НЕ удалось и что НЕ проверено - **Консент-гейт движка живьём НЕ пробит деньгами**: на $0-ценах local-пары проекция всегда $0 и под порогом — живой отказ «over the consent threshold» требует ненулевого прайса (живого ключа или прайс-таблицы с ценой). Консент-половина доказана юнитами (argv, сумма, отказ роста) и движковым контрактом (`--accept-rebill=` ниже проекции — отказ; текст `rebill.go:322` сверен), НЕ живым прогоном. Требует стенда с ненулевым прайсом — кандидат строки 202. - **`ur.at >= r.started_at`** сравнивает движковое event-время с платформенным `now()` одного хоста; на разъехавшихся часах мульти-хостового будущего полоса пере-прохода может недосчитать главы (транзиентно, до следующего касания). Названо в комментарии `rePassDone`. - **Идемпотентный ключ повторного `POST /runs {re_pass}`** — не строился (как и у обычного Start вне идемпотентности ключа запроса); повтор после успеха отвечает `run_in_flight`/`ErrRePassUnavailable` (факт погашен финишем) — вырожденных дублей не нашёл, но специального пина нет. - Опровергатель §3.3 — спавнен, вердикт аддендумом (на момент записи ещё бежал). ## ПАК P10 — ЗАПИСКА-ПЛАН (сессия платформы, 28.08, ДО правок; промт `docs/PLATFORM_P10_SESSION_PROMPT.md`, D39.165) Карта чтения промта пройдена целиком; все движковые факты промта пере-проверены чтением (`stagerun.go:52-58` гард и `:88-107` $0-репин · `rebill.go:322` плоский отказ и `:38` порог · `status.go:202-209` смета · `main.go:252-262` ортогональность и кумулятивность капа · `book.go` промпты/langpack в снапшоте) — расхождений с промтом НЕТ. ### Выбранные формы (обоснование — рядом; посадочная проверка в §4 промта) 1. **Носитель факта «банк двигался» — колонки на `books`**: `bank_moved_at timestamptz` (момент успешного apply с `changed:true`; превью и no-op НЕ пишут) + материализованная смета `rebill_units int` / `rebill_usd_micro bigint` (снятая `tmctl status` в самой двери правок сразу после apply, под тем же `lockBook`, $0). **Предикат факта: `bank_moved_at > finished_at ПОСЛЕДНЕГО прогона** (или прогон отсутствует ⇒ факта нет — все джобы будут свежими). Эта семантика закрывает все три края промта БЕЗ спец-случаев: правка в окне `awaiting_bank` легла ДО финиша того же прогона ⇒ факт не встаёт ⇒ resume без флагов, гард молчит (edit-джобов нет — эррата 28.08-и); правка после паузы потолком посреди редактуры ⇒ факт стоит ⇒ resume несёт флаги; правка на дочитанной ⇒ факт стоит ⇒ дверь §3.2. Успешный финиш пере-прохода гасит факт сам (новый `finished_at` > `bank_moved_at`), без отдельного сброса. 2. **Стабильность argv при респавне — по образцу `verify_bank`**: решение о флагах принимается ОДИН раз в Start/Resume под `lockBook` и пишется в строку прогона (`runs.resnapshot bool`, `runs.accept_rebill_micro bigint`); `spawn.spec` читает строку. Флаг не может появиться посреди прогона по построению. 3. **Различение банкового сдвига от деплойного — сравнением СМЕТ, не чтением снапшотов** (снапшоты — словарь движка и через шов не ходят). Смета материализуется В МОМЕНТ правки банка — это чистая цена БАНКОВОГО сдвига. На Start/Resume под мьютексом смета пере-снимается живьём ($0); если живая проекция ВЫШЕ материализованной сверх допуска — сдвиг не (только) банковый (деплой двинул промпты/langpack) ⇒ **явный отказ 409 ДО холда** с ремеди «пере-смотри смету» (обновить материализованную = пере-показать пользователю). Согласие движку — ВСЕГДА `--accept-rebill=<материализованная сумма>`: сумма, которую видел пользователь; движковый гейт «потолок ниже проекции — отказ» остаётся вторым рубежом. «Свежая квитанция» сама по себе основанием не является — основание всегда КОНКРЕТНАЯ согласованная сумма (D20.2-Q2). 4. **Дверь §3.2**: допуск в `Start` — на книге с фактом шкала не отбивает старт; **холд = строго положительное приращение от СМЕТЫ** (`max(rebill_usd, пол в 1 цент)` — не главы×$0.03; пере-проход стоит центы, незатронутое $0), `bookCap = committed + этот холд` (кумулятивная семантика не трогается). 5. **Шов §3.3**: `rebill_units`+`rebill_usd` в аллоулист `StatusReport` (оба, по поправке промта; доллары в micro-USD на шве, как `Spend`; наружу в API — только юниты). Опровергатель на пропуск — заказ §4.5, спавню с мандатом «покажи, где это нарушает §2.12 или D39.84». ### Открытые оси (форма решится при исполнении; затык = пинг, не интерпретация) - **Как клиент ПРОСИТ пере-проход и что показывает полоса такого прогона.** Дочитанная книга: `Scale.Max=0`, валидного `ceiling_chapters` у клиента нет. Черновая форма: options объявляет `rebill_units>0`, Start принимает пере-проходную форму запроса (внутренне `ceiling_chapters=0` легализуется ТОЛЬКО при факте) — но `runTotal=0` полосе запрещён, полоса пере-прохода — из юнитов сметы. Это контрактная половина — опишу предложением §3.4, до слова оркестратора провод не трогаю. «Вид покупки» на провод НЕ несу (D39.163: «третьего исключения нет»); если форма без него не встанет — довод по существу отдельно. - Допуск против «частично дочитанной» книги (остаток >0 И банк двигался): обычная покупка уже доступна — несёт ли она флаги? Да (мина бьёт по продолжению с edit-джобами) — это §3.1, не §3.2. ### Порядок работ (§3.1 первым, как заказано) 1. Миграция (3 колонки books + 2 колонки runs) → 2. запись факта+сметы в двери правок → 3. флаги в Start/Resume + `TranslateArgs` → 4. репро мины на ЖИВОМ tmctl (edit-джоб ДО правки — состояние эрраты; прецедент `bankapply_live_test.go`) → 5. дверь допуска + холд от сметы → 6. аллоулист шва + опровергатель → 7. options/предложения §3.4 в журнал → 8. посадки, батарея (скипы с DSN и без — командой), оси ревью 1–3. ### Не-делать (из промта, себе) `backend/` и `docs/` не трогаю · $0.03 не трогаю · цикл пост-ридинга гейчен (§0-бис) · чужие 20 позиций полигона · `docs/PROGRESS.md` не пишу. ## ФИКС-РАУНД ПО ДИСПОЗИЦИЯМ ОРКЕСТРАТОРА ИСПОЛНЕН — 13 фиксов, 9/9 посадок пойманы, одна находка воркфлоу ОПРОВЕРГНУТА исполнением, канон 0.6.0 принят (сессия платформы, 28.08, после записи ниже) Диспозиции пришли двумя сообщениями оркестратора (28.08) + третьим — канонная половина полосы (0.6.0). Всё исполнено, кроме ДВУХ пунктов с несогласием (аргументы ниже — обе позиции по норме «несогласие говори»). ### Фиксы в дереве (сверх сданного пака; каждый с пином, посадки — в копии `~/tm-p9-mut2`) | # | Что | Пин | Посадка | |---|---|---|---| | F1 | **MAJOR(а) 1 МиБ**: `ingest.EncodeDecisions` рендерит с `SetEscapeHTML(false)` (документ читает движок, не браузер; выбор обоснован в комментарии) **+ жёсткий гейт**: рендер сверяется с зеркалом движкового капа `ingest.MaxDecisionsDocument` ДО спавна → `ErrBankDocumentTooLarge` → **413** (остаточная конвертная полоса ~40 байт закрыта гейтом, слово всегда канонное) | `TestTheRenderedDocumentDoesNotInflateEscapableBytes` (ingest) · `TestARenderedDocumentOverTheEngineCapIsRefusedBeforeTheSpawn` (runs, движок НЕ спавнится) · `TestARenderedDocumentOverTheEngineCapAnswers413` (httpapi) | M6 (возврат `json.Marshal`) и M7 (снятие гейта) пойманы топично | | F2 | **MAJOR(б) мьютекс**: `lockBook(ctx)` — одноместный канал вместо `sync.Mutex`, ожидание наблюдает контекст; **бюджет двери ставится ДО очереди** (накрывает ожидание+вызов); Start/Resume передают свои ctx; отмена в очереди корректно декрементит refcount | `TestAWaiterWhoseContextEndsLeavesTheBookQueue` (+ leak-тест расширен) | M11 (ожидание игнорирует ctx) поймана | | F3 | **Р1 окно стопа**: `ReadBookForRun` вырос полем `LiveRunAwaitingBank` (живая строка в `awaiting_bank`); предикат двери — `HasLiveRun && !LiveRunAwaitingBank`; Start по-прежнему держится на `HasLiveRun` (вторая строка сломала бы `runs_one_live_per_book`); худший случай окна — движок ещё дожёвывает → честный класс 12 → 503 «повтори». Довод в комментарии переписан, противоречие с `reconcile.go:1122` разрешено в пользу кода | `TestTheDoorOpensOnTheSigningStopWindow` (фикстура — как `control_test`: журнальный `awaiting_bank` без `finished_at`); обратная сторона держится старым `TestCorrectionsRefuseWhileTheBookIsBeingTranslated` | M8 (старый предикат) поймана — refuse-тест при этом остался зелёным | | F4 | **Р2 миграция 00026**: два UPDATE сведены к ОДНОМУ — `draft_before := chapters_before` для ВСЕХ строк (та аппроксимация, которую ревью само проверило как самосогласованную для finished): пере-снятие БАЗ ПОСЛЕ работы прогона — единственный источник вечного недоезда −2·p_e — удалено; заодно умер и dispute про READ COMMITTED между двумя стейтментами (стейтмент один). Остаток по эпохам старого `chapters_before` честно назван в комментарии миграции. `migrations.sha256` пере-подписан (миграция не релизнута — слово оркестратора) | пин исполнением невозможен (тестовые БД наливаются миграциями ДО данных — старая семантика юнитом задним числом непроверяема); держится формулой + комментарием | — (названо, не скрыто) | | F5 | **Р5 presence-vs-null**: все строковые члены `wireCorrection` — `nullableString` (парный `nullableInt`); явный `null` на любом = malformed ТИПА (рефьюз до правил присутствия); правила присутствия (`id`×tuple, `dst`/`kind` на decline) считают КЛЮЧИ | +3 кейса в `TestACorrectionRequestIsValidatedWholeWithPointers` (null-src при id · null-dst на decline · null-action) | M10 (снятие null-гейта) поймана | | F6 | **Р6(i)**: комментарий SIGKILL-ветки переписан честно (ветка достижима только когда SIGTERM НЕ отработал; полу-приземлённая пара без отчёта — остаток в PD-407) | — (комментарий) | — | | F7 | **Р6(ii)**: `bankStopGrace` 10 с → **30 с**, число обосновано замером движка в комментарии (11.3 с непрерываемого фолда на документе-максимуме, ×2.5 запас на медленный хост) | — (константа с доводом) | — | | F8 | **Р6(iii)**: связка «ручка `TM_PLATFORM_RUN_BUDGET` × движковый кап 5000×11.3с» и цена понижения названы комментарием у бюджета двери (механизм не строился — слово оркестратора) | — | — | | F9 | **Р7**: `Service.SweepCorrectionScratch()` — подметание `bank-corrections-*.json` на буте (вызов в композиционном корне `tmplatformd` ДО подъёма HTTP), файлы вне маски не трогаются | `TestBootSweepsOrphanedCorrectionDocuments`. ⚠ Честно: вызов ИЗ main юнитом не запинен — мутация «не звать на буте» ловится только чтением | M14 (маска мимо) поймана | | F10 | **Д5**: комментарий `spawn.go` о сбросе `--verify-bank` переписан с упразднённого D39.144-контракта на настоящее основание (память v16 покрывает карту; старое основание питало дырявый гард — названо в комментарии) | — | — | | F11 | **Н5 hello**: при `resuming` hello несёт `last` клиента, не голову истории (свежий коннект — голову, как и `from` четырьмя строками ниже) | `TestAResumingHelloCarriesTheClientsOwnWatermark` (+ свежий коннект отдаёт голову) | M13 поймана | | F12 | **Н1-гард**: `Resume` под мьютексом сверяется с новым `pgstore.LatestRunID` (тот же порядок, что `lastRun`) — не-последний прогон получает `ErrNotResumable` с человеческим доводом; реконсилерский RestartRun в гарде не нуждается (живой прогон всегда последний по `started_at`: пока он жив, новый не стартует) | `TestAnOlderRunCannotBeResumedOverANewerOne` (Now сдвигается между стартами — фикстурный Now дал бы одинаковый `started_at` и флейк по случайному id) | M12 поймана | | F13 | **Канон 0.6.0** (третье сообщение): `ContractVersion` → 0.6.0; комментарий `wireProgress` переписан (сквозная доля и `stage` теперь КАНОННЫ, открытый словарь; оба условия исключения шапки — вывод платформой из тех же счётчиков и открытость — коду отвечают, проверила); протухший абзац про `stop_requested` заменён на «ни одного члена впереди канона»; комментарий у теста строки библиотеки сужен до правды (сам тест не тронут) | гейт версии (`reading_test`) снова зелёный | — | ### Два НЕСОГЛАСИЯ с диспозициями (норма «говори») 1. **Р4 (`limitedBuffer` не убивает чайлда) — находка воркфлоу ОПРОВЕРГНУТА исполнением, фикс ОТКАЧЕН.** Посадка M9 (kill вырезан) прошла пин за **0.09 с** — чайлд умер сам; исходник Go называет механизм прямо: копирующая горутина exec закрывает читающий конец при ошибке Write (`os/exec/exec.go` writerDescriptor: «pr.Close() // in case io.Copy stopped due to write error») → EPIPE на следующей записи. Агент экстраполировал семантику `StdoutPipe` (там `drain()` Kill действительно нужен — exec ничего не закрывает за ручного читателя) на `Stdout=io.Writer` и НЕ исполнял. Мой добавленный было kill снесён (он был бы кодом под ложный довод); исходный комментарий `limitedBuffer` был ВЕРЕН и расширен ссылкой на опровержение; поведение запинено живьём: `TestAnEndlessBankApplyIsRefusedRatherThanRead` (0.1 с; посадка M9b «кап перестал отказывать» валит его таймаутом). Это второй случай за ревью, где замер бьёт рассуждение — в обе стороны. 2. **Р2-dispute (READ COMMITTED между двумя UPDATE миграции) — строкой НЕ заведён:** фикс F4 свёл миграцию к одному стейтменту, окна больше не существует. ### Регистр и якоря - `PD-400.2` пере-описана честно (снято «ни ложного слова», мульти-репличный суб-кейс с ложным 503 назван прямо; оговорка внесена и в комментарий ветки класса 12) — редакция для акта лендинга. - Новые строки: **PD-402..PD-406, PD-410** (major: resume не-последнего [read-половина, write-гард закрыт F12] · sticky `edit_wave`/re-sell · `chapters_done` назад · бездеревная книга С ЗАМЕРОМ достижимости 0.018 с из лога стенда · SSE hello [закрыта F11 — оркестратору решить статус] · Р3-полоса/план движка — АРХИТЕКТУРНОЕ, отдельным паком), **PD-407, PD-409, PD-411..413** (minor: SIGKILL-слово+грейс [грейс закрыт F7] · утечка temp-файлов [закрыта F9] · мёртвые колонки классом A второго яруса онтологии-18 · карточка 5 сканов · emitProgress под блокировкой), **PD-408** (info: ручки двери). Статусы строк, чьё лечение легло этим раундом (405-замер/406/407-часть/409), НЕ переводила — закрытие актом лендинга, как у PD-401. - Гейты регистра: `counts.py --check` → **413 строк, 109 открытых (9/33/67), битая форма [], хвост []**. Якоря, сдвинутые МОИМИ правками, пере-нацелены (PD-375 `runs.go:269`, PD-*` books.go:1065`). ⚠ Для оркестратора: `docs/PROGRESS.md:159-160` и `17-seam-inbound-law.md:59` / `25-seam-cold-review.md:7` держат якоря, уехавшие НЕ моими правками (supervisor/book.go — чужие сдвиги) и моей (reconcile.go:1228 → теперь `:1246`) — файлы твоей зоны, чинить тебе. ### Числа финальной батареи фикс-раунда (каждое — командой) `go test ./... -race -count=1 -v` с тремя гейтами → **EXIT=0, 18 пакетов ok, `grep -c -- '--- SKIP'` → 0, `grep -c '^=== RUN'` → 793** (было 781 на сдаче ревью — +12 новых пинов), `FAIL|DATA RACE` — 0 вхождений; `golangci-lint run` → **0 issues**; `gofmt -l` пусто; `go vet` чисто (в составе линта). ⚠ Промежуточная батарея №2 имела РОВНО ОДИН честный FAIL — старый пин `TestResumeIsRefusedWhenTheBookHasAnotherLiveRun` поймал, что первый вариант гарда F12 перекрывал канонное слово `run_in_flight` при живом чужом прогоне; чинился КОД (словоразделение: живой сосед → `run_in_flight`, финишировавший → `ErrNotResumable`), тест не тронут. Опись дерева: `git status --short -- platform/` → **50 путей (39 M + 11 ??)**. ## ВОРКФЛОУ-РЕВЬЮ ДЕРЕВА P9 ОТРАБОТАНО — 16 линз, оба отложенных MAJOR подтверждены замером, сводка находок для оркестратора (сессия платформы, 28.08) Заказ владельца (релей 28.08): адверсариальная вычитка дерева воркфлоу-оркестрацией — полоса ×4, дверь ×3, миграция, раскладка кодов (`wf_cac14b2f-e84`) + гонки данных по 4 траекториям владельца, баг-хант нового кода, стоимость per-request (`wf_155de7c3-bb4`). Раскладка моделей — по слову владельца: 1×Fable на воркфлоу (самая тяжёлая линза), остальным явный opus/sonnet. 16/16 агентов дошли (0 ошибок), ~2.83M токенов. Мандат: «найди, где рассуждение неверно», каждая находка — severity + file:line + траектория с числами; PD-281, PD-401-остаток и пере-нарезка исключены заданием как названные границы. **По находкам НИЧЕГО не чинил — жду слова оркестратора.** Дисциплина честности: пометка «подтверждено прогоном» в траекториях — исполнение АГЕНТОВ (их overlay-тесты и /tmp-замеры в копиях дерева), не моё; я пере-исполнил чтением два клейма своего кода (Р4, Р5 ниже — оба подтвердились) и одним своим EXPLAIN-замером ось стоимости (сошлось, ниже). Полные результаты с траекториями сохранены вне репо: журналы воркфлоу в каталоге сессии (`subagents/workflows/wf_*/journal.jsonl`). ### Два отложенных MAJOR ревьюера приёмки — оба ПОДТВЕРЖДЕНЫ, чинить - **MAJOR(а) «1 МиБ на двух документах» — ПОДТВЕРЖДЁН тремя агентами с независимыми замерами.** Механизм: `json.Marshal` в `ingest.EncodeDecisions` HTML-экранирует `&`/`<`/`>` (1 байт → 6) и меняет конверт (+~25 Б), так что тело под проводным капом 1 МиБ рендерится в документ НАД движковым капом 1 МиБ. Замер агента: 5000 declines с `note` из `&`×160 → тело 1 030 050 Б → рендер 5 030 075 Б → движковый exit 14 → **409 `bank_corrections_refused` «split it» вместо канонного 413**. Полоса законных тел широкая (~550 КБ плотного спецсимволами тела уже перескакивает), а существующий пин `TestAnOversizedCorrectionDocumentAnswers413` дыру не ловит — набит кириллицей, которая НЕ экранируется. ⚠ Конфликт линз: два агента первого воркфлоу положили эту ось в «не опровергнуто», рассуждая от omitempty («рендер меньше тела») и НЕ меряя; замер двух других бьёт рассуждение (один из «опровергателей» сам оговорил условие плотности `&<>`). Бонус-cosmetic туда же: скраб пути (`runs/bank.go:146`) на этом отказе даёт задвоенную фразу «the decision document the decision document» и пропускает на провод движковый префикс `pipeline:`. - **MAJOR(б) «мьютекс без контекста, бюджет после захвата» — ПОДТВЕРЖДЁН** (`runs/bank.go:94` vs `:126`, `lockBook` без ctx `:270-291`): K одинаковых ретраев дают очередь K×60 с; отменённый клиентом запрос ЖДЁТ в очереди и, дождавшись, делает работу (чтение книги, temp-файл) за мёртвый запрос; в ту же очередь встают Start и Resume — заявление «ждать один вызов движка» (`runs.go:242-246`) держится только для одного ожидающего. Дёшево лечится ожиданием с наблюдением ctx (канал вместо `sync.Mutex`). ### Находки в МОЁМ коде P9 (сверх двух MAJOR; лечение — после слова оркестратора) | # | Вес | Что | Где | |---|---|---|---| | Р1 | breaks | **Окно стопа подписи**: `bank_stop` ставит `awaiting_bank` БЕЗ `finished_at`, а `HasLiveRun` читает `finished_at is null` → до ближайшего свипа (15 с штатно; до 30 мин на бэкоффе отсрочек) дверь отвечает **409 `run_in_flight` на экран подписи, который платформа сама только что объявила**; соседняя кнопка Resume в том же окне отказывает ДРУГИМ словом («being settled»). Мой довод в `runs/bank.go:110-112` («its run is closed») прямо противоречит `reconcile.go:1122`; состояние строится собственным тестом дерева (`control_test.go:598-603`). Нашли НЕЗАВИСИМО 3 агента | `runs/bank.go:109` | | Р2 | breaks | **Миграция 00026 для прогонов в полёте**: пере-снятие баз посреди прогона, уже сделавшего p_e правок, оставляет его финал на `runDone−runTotal = −2·p_e` — полностью доделанный прогон закрывается, например, 5/7 навсегда. Двух-базовая схема корректна ровно потому, что базы берутся ДО работы прогона; миграция — единственное место, берущее их ПОСЛЕ. + dispute: READ COMMITTED между двумя UPDATE допускает двойное касание строки при конкурентном финише (при штатном деплое одной реплики окно закрыто остановкой старого демона — вес мал) | `migrations/00026:20-27` | | Р3 | breaks | **`draftWork` не моделирует порядок волн движка**: continuation над недочерновленной книгой (`draft_before ≥ chapters_before+C`, напр. прогон-предшественник упёрся в потолок на черновике) даёт `draftWork=0`, движок продолжает ЧЕРНОВУЮ волну — полоса 0/C весь прогон, stage «editing» на прогоне, который только черновит. Зеркало дефекта, который draftWork чинил | `readmodel.go:460` | | Р4 | breaks | **`limitedBuffer` НЕ убивает чайлда** при переполнении stdout — вопреки своему комментарию (контраст: `drain()` в `engine.go:262-272` при том же переполнении явно зовёт `Process.Kill`); чайлд виснет на записи в полный пайп до чужого дедлайна (~70 с под пер-книжным мьютексом), без дедлайна — навсегда. Подтвердил чтением. + dispute: stderr — НЕограниченный `bytes.Buffer` (не-тот бинарь по сконфигурированному пути может раздуть демона до OOM; читается всё равно только первая строка) | `runner/bankapply.go:98-111` | | Р5 | breaks | **Presence-vs-null дыра валидации**: только окна (`nullableInt`) различают явный `null` от отсутствия; `src/sense/dst/kind/id` — голые `*string`, так что `{"id":"X","src":null}` и decline с `"dst":null,"kind":null` проходят гейты, которые канонный `oneOf`/`not-required` велит отбивать 400. Подтвердил чтением (сам вводил `nullableInt` только для окон). Порчи данных нет — лишнее значение не читается; дыра контрактная | `httpapi/bank.go:48-51, 221-224, 251-259` | | Р6 | dispute | **Раскладка на ветке SIGKILL**: `ctx.Err()`-ветка (`runs/bank.go:132`) достижима ТОЛЬКО когда SIGTERM не отработал (процесс убит по `WaitDelay`) — а это единственный путь, оставляющий полу-приземлённую пару БЕЗ отчёта, т.е. адресат `bank_corrections_incomplete`, отвечаемый общим 503; мой комментарий описывает соседнюю ветку. + `bankStopGrace` 10 с КОРОЧЕ самого длинного непрерываемого участка движка (11.3 с на документе-максимуме) — грейс истекает до фазы записи. + `runBudget` — операторская ручка свипа: понижение (к чему подталкивают комментарии) делает легальный документ-максимум навсегда неприменимым (вечный 503 вместо «split it») | `runs/bank.go:132`, `runner/bankapply.go:40` | | Р7 | cosmetic | Утечка `bank-corrections-*.json` в StateDir при нечистой смерти демона (SIGKILL/OOM/обрыв грейса деплоя): cleanup только на defer, никто каталог не подметает; до 1 МиБ пользовательских решений на файл, бессрочно | `runs/bank.go:250` | | Р8 | — | **Формулировка PD-400.2 (accepted-risk) требует правки**: на мульти-реплике класс 12 может быть НАСТОЯЩИМ многочасовым прогоном соседней реплики (её Start видит свой мьютекс и чистый `runs`) — слово 503 «transient holder» и мой лог тогда ЛОЖНЫ, канонно верное слово — 409 `run_in_flight`. «Ни денег, ни порчи» держится; «ни ложного слова» — НЕТ. Риск остаётся принятым (v1 = одна реплика), но акт лендинга должен описать его честно | `runs/bank.go:27-39, 182-189` | ### Находки в НАСЛЕДИИ платформы, вскрытые ревью (не код P9; полоса и дверь на них стоят) | # | Вес | Что | Где | |---|---|---|---| | Н1 | breaks | **Resume НЕ-последнего прогона** (Resume пропускает любой `stopped` без проверки последнести; `lastRun` = `started_at desc`; RestartRun не трогает `started_at`): (i) полоса возобновлённого открывается на ЧУЖОЙ работе — 2/2 при нуле своих пассов (клэмп режет только >1, не двойной счёт до 1); (ii) карточка, кадр статуса и КАЖДЫЙ progress-кадр живого прогона считаются по ЧУЖОМУ финишировавшему — «ready» и замороженный бар, пока живой прогон тратит деньги. 4 агента, overlay-прогоны | `reconcile.go:1216`, `readmodel.go:487`, `runs.go:843` | | Н2 | breaks | **`edit_wave` sticky-true × оператор убрал редактора**: прогон делает 100% купленного и закрывается ready на 50% со stage «editing»; `chaptersDone` (edit-колонка) мёрзнет → `ChaptersLeft` не падает → **шкала повторно продаёт уже переведённые главы** (симптом PD-202, записанной fixed); follow-up прогон над этой покупкой читается 0/C с первой секунды. Обе фразы комментария `sink.go:406-409` о собственном коде ложны | `sink.go:415`, `readmodel.go:388` | | Н3 | breaks | **Флип false→true уводит `Book.chapters_done` НАЗАД** (finishedUnits через ЖИВОЙ флаг): 40/100 → 0/100 одной транзакцией при росте ревизии — клиент обязан отрисовать спад; `ChaptersLeft` раздувается обратно → предлагает купить купленное. PD-316-класс на непокрытом триггере (пин ловит только admission). Крайний случай: книга, полностью начерченная под false, НЕдочитываема редактором никаким действием API (ChaptersLeft=0 → прогон не допускается → флип недостижим) | `sink.go:415`, `readmodel.go:388`, `books.go:981-983` | | Н4 | breaks | **Книга без материализованного дерева**: полоса (вся из `chapters`) читает 0/total весь прогон при исправно доезжающих progress-событиях; комментарий `sink.go:219` («счётчики придут из progress-события») ложен уже в HEAD. + dispute: `runs.draft_done/draft_total/edit_done/edit_total` — 2 писателя, 0 читателей (PD-314-класс, лишняя запись на каждом событии под блокировкой книги; обоснование `greatest()` на `sink.go:444-446` защищает несуществующую полосу) | `sink.go:219, 137, 447` | | Н5 | breaks | **SSE `hello` при переподключении несёт `id = state.Position` вместо предъявленного `last`** — WHATWG-клиент фиксирует новый Last-Event-ID ДО получения догона; обрыв сразу после hello теряет кадры навсегда (включая `note`, которые контракт запрещает терять) и обезоруживает дельта-ремонт (`revision` из hello выше потерянных строк). Лечение — одна строка: при resuming слать `last`. Код рядом (`stream.go:122-126`) сам знает правильное значение | `stream.go:99` | ### Находки зоны ДВИЖКА (чужая зона — пинг оркестратору, мне не лечить) | # | Вес | Что | Где | |---|---|---|---| | Д1 | breaks | **Decline не энтити-широк на обратном пути**: отклонённая сущность возвращается в карту/auto-bank/редактору через свой АЛИАС (`reverseSectionTerms` фильтрует по одному ключу, а `mined_rejects` получает только `src`), и память предъявления глушит стоп об этом НАВСЕГДА — нарушен собственный контракт «a declined term never re-enters» (`miner_emit.go:59-63`); approve симметричной утечки не имеет (кластер через строку банка) | `mining.go:548`, `membank/decisions.go:598` | | Д2 | breaks | **Неидемпотентный decline** поверхности подписанного сида, имеющей строку в дельте: первый вызов ПРИНЯТ, повтор ТОГО ЖЕ документа — 409 (гейт `decisions.go:374` судит ДО-состояние, которое свёртка сама стирает); при классе 15 предписанный ре-сенд отбивается ЦЕЛИКОМ — **обещание сходимости 503-ретрая, на котором стоит синхронная дверь, ломается**. Доказано исполнением через СОБСТВЕННЫЙ оракул репозитория (оракул 4 фаззера); фаззер структурно не достаёт (фикс-книга не пересекает сид с дельтой). Лечение: судить по ПОСТ-состоянию, как соседний `refuseInertDeclines` | `membank/decisions.go:374` | | Д3 | breaks | **Потеря/порча маркера выхода после стопа** (совместная с платформой): рестарт/ретрай проходит границу банка НАСКВОЗЬ — память предъявления покрывает карту, движок «continuing» одним WARN себе в журнал — оплаченный `verify_bank` стоп исчезает молча и навсегда (память append-only). Гард `LiftBankStop` (`reconcile.go:1122-1126`) писан против движка ДО памяти v16 и этот путь не держит | `reconcile.go:424`, `mining.go:216-227` | | Д4 | dispute | **`signature` в квитанции двери** считается от карты, которую переписывает ЛЮБАЯ граница майнинга (запись карты на `mining.go:191` — ВЫШЕ решения о стопе): `signature != null` не означает состоявшегося стопа; `surfaces`/`undecided` дрейфуют между двумя вызовами владельца; `undecided: 0` достижим при непредъявленных решениях (кап top-200 вытесняет). У карты ЕСТЬ идентификатор (`signaturemap.go:25-29`) — шов его не читает | `pipeline/bankdecisions.go:549-598` | | Д5 | cosmetic | `spawn.go:159-165` (платформа, но про движок) цитирует упразднённый D39.144-контракт стопа («halts whenever undecided terms remain») — при памяти v16 фактически ложно и питает дырявый гард Д3 | `spawn.go:159-165` | ### Ось «стоимость» — мои живые замеры + вердикт агента (сошлись) - **Строка 186 единого бэклога ПРОТУХЛА — рекомендация оркестратору: закрыть.** Механизм построен и применён: агент прогрепал ВСЕ call sites `writeJSON` (коллекции, карточка, capabilities, POST двери; SSE корректно НЕ сжимается). Мои замеры на стенде tmp9stand (команды и заголовки — сырьём): банк 437 Б / 7.2 мс холодным, повтор с `If-None-Match` → **304 / 0 Б / 2.6 мс**; юниты главы 3008 Б → **740 Б gzip (×4.1)**; gzip ниже порога 1024 Б честно не применяется; resume SSE за головой → 204 / 1.3 мс. Взамен предлагаю ДВЕ новые стоимостные строки (ниже). - **Карточка книги — 5 коррелированных сканов `chapters` на один GET** (3 в runRow: draftChapters ×2 + editChapters, +2 в bookColumns: chaptersDone + noteCount), и те же 2 — на КАЖДУЮ строку страницы библиотеки (×100). Эмпирика агента на живом PG 18.4, фикстура в форме миграции 00002, книга 2283 главы: как написано — 1.359 мс; те же числа одним LATERAL `count(*) filter(...)` — 0.307 мс (×4.4). Мой контрольный EXPLAIN на реальной схеме tmp9stand: **6 SubPlan-ов в плане, 5 исполняются** (ELSE-ветка never executed) — форма подтверждена. PG повторные текстовые вхождения подзапроса НЕ дедуплицирует (доказано side-effect-последовательностью), CASE-ветки честно short-circuit. - **`emitProgress` повторяет 3-скан агрегат по ВСЕЙ книге на каждое progress-событие** (движок шлёт его на каждый разрешённый юнит каждой волны) — ПОД блокировкой строки книги: O(глав × юнитов) вместо O(юнитов), на большой книге — секунды суммарного удержания блокировки за прогон. - cosmetic: `MkdirAll(StateDir)` на каждый вызов двери — место одному разу в конструкторе Service. ### Чистые оси (проверено — не опровергнуто) Линза `bugs:service-and-seams` — **0 находок** (проверены: cleanup temp-файла по всем выходам; редакция refusals — единственная ветка с путём; вердикт-таблица против всей полосы exit.go; ключи только на translate; refcount lockBook; SpendBaseline из колонок ПОПЫТКИ; согласованность bankCountsTx; проекции без утечек словаря/путей; build/vet/тесты). Сквозные not_refuted (по многу агентов): взаимоисключение «дверь × спавн» в заявленную сторону ДЕРЖИТСЯ на одной реплике (все 5 путей спавна упираются в `finished_at is null` = предикат HasLiveRun); правило одного писателя двух файлов решений; сходимость повтора того же/другого документа при ЦЕЛОМ гейте Д2; SIGTERM после записи не теряет квитанцию (`Exited=true` глотает ctx.Err — совпадает с диском); идентичность термов через пересборку банка (id из ключа уникальности); сериализация чеканки кадров и штамповка ревизий чисты; чтения на одном снимке; идемпотентный ключ Start не клинит за очередью мьютекса. ### Финальная батарея по дереву (после всех приёмочных правок; дерево ревью НЕ меняло) `go test ./... -race -count=1 -v` с тремя гейтами → **EXIT=0, 18 пакетов ok, SKIP=0, `^=== RUN` = 781**; DATA RACE — нет. (На сдаче было 775 — рост на приёмочных доборах, число командой.) ### Вопросы/предложения оркестратору 1. Диспозиция Р1–Р8: что чиню в этом дереве до лендинга, что строками регистра. Готов завести пакет строк PD-40x по всем группам после твоего вердикта (не завожу до слова — рядом лендинг). 2. PD-400.2: скорректировать формулировку accepted-risk по Р8 (снять «ни ложного слова», описать мульти-репличный суб-кейс) ДО акта лендинга. 3. Строку 186 закрыть (замер выше), взамен — две стоимостные строки (5-скан карточка ×100 на библиотеку; emitProgress O(глав×юнитов) под блокировкой) куда решишь. 4. Д1–Д5 — пинг бэкенду твоим каналом; Д2 ломает обещание, которое МОЯ дверь даёт на проводе (сходимость ретрая 503) — до его лечения в движке слова канона §applyBankCorrections о ретрае верны не для всех документов. 5. Н1–Н5 — наследие: Н1 (resume не-последнего) и Н5 (hello id, лечение в одну строку) выглядят дешёвыми и болезненными; Н2/Н3 упираются в продуктовое решение о смене формы конвейера на живой книге. ## ПАК P9 ОТРАБОТАН — дверь правок банка смонтирована, ключи едут, полоса сквозная; цепь живого прогона ПРОБИТА живьём (сессия платформы, 27.08) Дерево передаётся на лендинг. Опись: `git status --short -- platform/` → 31 изменённый + 9 новых файлов, все в зоне; вне `platform/` не тронуто ничего. ### Таблица комплектности против §3 (пункт → сделано → каким ИСПОЛНЕНИЕМ подтверждено) | §3 | Что сделано | Исполнение | |---|---|---| | §3.1 дверь | `POST /v0/books/{bookId}/bank/corrections` в `contractSurface` (монтаж по `Deps.Bank`, кап тела = канонный 1 МиБ = `DefaultMaxBody`); строгий декод (`DisallowUnknownFields` + запрет хвостовых байт), вся канонная валидация формы с JSON Pointer'ами; `Capabilities.bank_corrections_enabled` = факт монтажа; словарь шва `ingest/bankdecisions.go` (запрос v1 / отчёт v2, аллоулист); канал `runner.BankApply` (прямой чайлд, SIGTERM-грейс, потолок чтения); вердикт `runs.bankVerdict`; квитанция-проекция с переводом `edit_wave→refinement` и отказом на неизвестное слово; `refusals[]` в конверте `Problem` + коды `bank_corrections_refused`/`bank_corrections_incomplete` | живой пробой — `docs/p9/door-live-probe.md` (превью → правка → ретрай `already_applied` → отказ 409 с указателем → resume → 409 `run_in_flight` при живом прогоне, тела дословно); юнит-пины `internal/httpapi/bank_test.go` (7 на сдаче; 9 после приёмочных доборов — превью и обрыв на потолке), `internal/runs/bank_test.go` (6), `internal/ingest/bankdecisions_test.go` (2); посадки M1, M2 | | §3.1 раскладка кодов | заказанное: 14→409 `bank_corrections_refused`+`refusals[]` · 15→503 `bank_corrections_incomplete` · 12→409 `run_in_flight` · тело>1МиБ→413 · >5000 и форма→400. Моя половина с доводом: **13→503 `service_unavailable`** (не мигрирован — оператор, транзиентно; различим от 15 по коду) · **19 и незнакомые члены полосы→503 `service_unavailable`** (рассинхрон сборок; какое из двух других ремеди — неизвестно по построению) · **10/11→500** (оба входа глагола рендерит платформа) · **exit 5 и таймаут бюджета→503 `service_unavailable`** (рестарт деплоя; SIGTERM-контракт глагола graceful). Три ремеди («пере-реши»/«повтори то же»/«позови оператора») не сливаются | пин-таблица `TestBankVerdictKeepsTheRemediesApart` + `TestTheDoorsFailuresKeepTheirRemediesApart`; посадка M1 (слияние 15 в 503-generic) поймана; опровергатель кодов: «(а) слияние ремедий — не опровергнуто по всем девяти строкам» | | §3.2 синхронность | вызов синхронный, бюджет = `runBudget()` (60 с — класс вызовов движка); пер-книжный мьютекс `lockBook` в `runs.Service`, его берут corrections И `Start` И `Resume` (Start — та же гонка спавна, что resume); проверка живого прогона — ПОД мьютексом; гейт готовности книги (`readyToTranslate`) — как у Start (находка опровергателя) | `TestAResumeWaitsOutALiveCorrectionCall` (resume ЖДЁТ живой вызов двери, канал-гейтед фейк); `TestCorrectionsRefuseWhileTheBookIsBeingTranslated`; живьём — шаг 11 пробоя (409 `run_in_flight` на живом прогоне); синхронность ДЕРЖИТСЯ: живой вызов двери на стенде — доли секунды, потолок глагола 5000 подобран движком под таймаут вызывающего | | §3.3 ключи | `TM_PLATFORM_ENGINE_KEYS_PATH` (абсолютный или отказ на буте; ⚠ суффикс `_PATH`, не `_FILE` — `*_FILE` в зоне значит «файл со значением секрета», гейт `TestEverySettingThisServiceReadsIsPrinted` это и поймал) → `runner.TranslateArgs` кладёт `--keys-file` ТОЛЬКО на `translate`; в окружение юнита ключи не кладутся; пусто = WARN на буте | `TestTheDeploymentKeyFileReachesTranslate`, `TestTheSpawnedUnitCarriesTheDeploymentKeyFile` (argv юнита + отсутствие ключей в Env); живьём: движок с несуществующим файлом падает громким «--keys-file … cannot be read», с файлом — пре-флайт пройден (лог пробоя); посадка M3 поймана | | §3.4 полоса | ОДНА монотонная доля через обе волны: `done = draftBar + lastBar`, `total = draftWork + ceiling` (редактор) / `ceiling` (без), где `draftWork = clamp(chapters_before + ceiling − draft_before)` — знаменатель считает работу ЭТОГО прогона (правка по находке опровергателя: continuation поверх начернённого задела кончал ready на 50%); две базы в `StartRun`, пере-базирование при снятии стопа УДАЛЕНО; подпись `progress.stage` (`drafting`/`editing`, открытый словарь); кадр `progress` и старт-квитанция несут то же; миграция `00026` (live-прогоны — обе базы пере-сняты верными предикатами, законченные — аппроксимация, названо в самой миграции) | живьём: 0/6 drafting → стоп 3/6 editing → resume 3/6 (БЕЗ обнуления) → ready 6/6 (лог пробоя); пины `TestTheBarIsOneMonotonicFractionThroughTheSigningStop`, `TestARunOverADraftedBacklogOwesOnlyTheLastPass`, `TestADraftOnlyDeploymentCountsItsOneWaveOnce`, `TestASecondRunsBarStartsAtZeroOverAHalfFinishedBook`, `TestTheRunsBarNeverExceedsWhatItBought`; посадки M4, M5 пойманы | | §3.5 PD-399 | `pending_decisions`/`complete` сняты со всех трёх носителей (`BankCounts`+кадр `EventBank`, `wireBankPage`, подзапрос к мёртвой `bank_decisions` ушёл); пин ПЕРЕПИСАН на отсутствие | `TestTheBankAggregatesRideOnTheFirstPageOnly` пинит ОТСУТСТВИЕ; живьём: `GET /bank` на стопе и после прогона — полей нет (лог пробоя, шаги 5 и 13); строка PD-399 → fixed | | §3.6 конвенция пути | `runner.projectDB()` и парс `book.yaml` УДАЛЕНЫ; путь банк-экспорта берётся из конверта `artifacts.bank_export`, который движок публикует в `manifest --json` (движковая половина — d1eb8a9); `refreshBank` кормится манифестом той же refresh-пачки; движок без конверта = громкий отказ, долг ретраится | `TestTheBankIsReadAtThePathTheEnginePublished`, `TestAnEngineWithoutTheEnvelopeIsAFailureRatherThanAnEmptyBank`; живьём: банк пробной книги материализовался по опубликованному пути (шаг 13) | ### Числа сдачи (каждое — командой) - Батарея с ТРЕМЯ гейтами (`TM_PLATFORM_TEST_DSN` · `_ENGINE_BIN`+`_BOOK_TEMPLATE` · живой пользовательский systemd): `go test ./... -race -count=1 -v` → **EXIT=0, 18 пакетов ok, `grep -c -- '--- SKIP'` → 0, `grep -c '^=== RUN'` → 775**; линтер `golangci-lint run` → **0 issues**; `gofmt -l` пусто, `go vet ./...` чисто. Лог — `~/tm-p9-work/final-battery.log` (вне репо). - Тест-функции зоны: `grep -rh '^func Test' platform --include='*_test.go' | wc -l` → **561 (HEAD) → 580**. - Регистр: `python3 docs/scripts/counts.py --check` → **401 строка, открытых 97** (на сдаче 3/27/67; после пере-взвеса PD-401 приёмкой — 3/28/66); было 398/96 — PD-399 закрыта, PD-400/PD-401 заведены; «битая форма: []», хвост чист. ⚠ Первая редакция этой строки называла «400 строк» — снято пере-счётом ревьюера, число выше — командой. - Миграции: `00026` добавлена, отпечаток в `migrations.sha256`; `pgstore.Migrate` гонялся каждой тестовой базой батареи (сотни накатов за прогон). - **Посадки мутаций — 5, пойманы 5/5, в копии с каноном** (`cp -a --parents platform docs/architecture/14-api-contract`, скрипт `~/tm-p9-mut/run-mutations.sh`, логи `~/tm-p9-mut/*.log`), вердикт по ДЕЛЬТЕ против чистой базы ТОЙ ЖЕ копии (база EXIT=0, FAILS пусто) и по ТОПИЧНОСТИ: | # | механизм | что посажено | что упало (ровно топичный пин) | |---|---|---|---| | M1 | дверь | класс 15 слит в `ErrBankUnavailable` | `TestBankVerdictKeepsTheRemediesApart` | | M2 | провод | кортеж перестал требовать `sense` | `TestACorrectionRequestIsValidatedWholeWithPointers` | | M3 | ключи | `Cfg.KeysFile` не доезжает до argv | `TestTheSpawnedUnitCarriesTheDeploymentKeyFile` | | M4 | полоса | знаменатель снова `2×ceiling` | `TestARunOverADraftedBacklogOwesOnlyTheLastPass` | | M5 | полоса | возвращено пере-базирование на снятии стопа | `TestADraftOnlyDeploymentCountsItsOneWaveOnce` | ### Живой пробой (§4.2) — цепь срослась, и за $0 Полный лог — **`docs/p9/door-live-probe.md`**. Суть: книга через живой интейк → прогон до банкового стопа → превью → правка → ретрай (`already_applied`) → отказ 409 с указателем → resume → 409 `run_in_flight` при живом прогоне → ready → **банк следующей границы несёт правку** (方源 → approved «Фан Юань-П9», задеклайненная поверхность исчезла из предложений). ⚠ Провайдер — локальная $0-заглушка (`local`-модель из `models.yaml`), потому что **в файле ключей деплоя нет ни одного живого провайдерского ключа** — движковый пре-флайт называл недостающие ключи по имени для всех шести облачных провайдеров (значения ключей в сессию не читались, гардрейл `.env` цел; замер — отказ резервации при потолке $0.000001, ДО вызова провайдера). Ось «деньги»: леджер стенда сверен ДВУМЯ путями на нетривиальном состоянии (3 холда/3 возврата/3 расчёта) — CLI `balance` и сырой SQL сошлись до цента ($25.000000). ### Опровергатели (§4, заказ) — 2 агента, обе панели принесли «ломает», всё абсорбировано 1. **Раскладка кодов.** «Ломает»: у двери не было гейта готовности книги (не-готовая книга доезжала до движка и возвращалась 500-ложью «наш дефект») — **починено** (`readyToTranslate` под мьютексом → 409 `book_not_ready`). «Спорно»: транзиентные держатели флока вне сериализации (границная материализация, ручной tmctl) читаются словом `run_in_flight`; мьютекс внутрипроцессный (мульти-реплика теряет сериализацию) — **заведено PD-400**, лечение вне заказа. Утечка пути темп-файла в `refusals[].detail` на движковых капах — **починено** (редакция пути в `ApplyBankCorrections`). Косметика: SIGINT вместо SIGTERM — починено (`syscall.SIGTERM`); пустой `rejected` при exit 14 — гард добавлен; хвостовые байты за JSON — отвергаются (`dec.More()`). ПРИНЯТО БЕЗ ПРАВКИ с причиной: `since_chapter: 3.0` (валидный integer по JSON Schema) реализация 400-ит — генерённые клиенты целых через точку не шлют, названная узость; item-код `missing` на присутствующем-но-пустом члене — словарь item-кодов открыт. 2. **Форма полосы.** «Ломает» №1: continuation поверх начернённого задела — ready на 50% с вечным `drafting` — **починено** (`draftWork` в знаменателе и в stage; пин + посадка M4). «Ломает» №2: бэкфил замораживал полосу легаси verify-прогонов — **починено** (миграция пере-снимает обе базы live-прогонов верными предикатами; для ЗАКОНЧЕННЫХ легаси — названная аппроксимация в тексте миграции). «Спорно»: флип `edit_wave` при живом прогоне двигает знаменатель — **заведено PD-401** (окно секунды, лечение трогает словарь шва). ПРИНЯТО С ПРИЧИНОЙ: пере-нарезка внутри прогона обнуляет полосу (снос resolutions — правда о пере-резанной книге, осознанное исключение); `stage='editing'` на банковом стопе (статус `awaiting_bank` на экране первичен); старт-квитанция читает «последний прогон книги» (вставка видна в своей транзакции, гонка требует регресса часов). ### Диспозиции по норме §3 п.8 (греп открытых строк по ПОЛНЫМ путям моих файлов — 46 совпадений) - **PD-399 — ЗАКРЫТА** этим паком (см. §3.5, пин назван в строке). - **PD-370 — предлагаю ЗАКРЫТЬ приёмке**: зонная половина закрыта 22.08, контрактная — минором 0.5.0 (D39.161: ноль вхождений отменённой модели в каноне), ратифицированная замена (дверь правок) построена этим паком. Закрытие — акт лендинга, не зоны: лекарство контрактной половины не в моём дереве. - **PD-281 — остаётся open, дописка внесена**: сквозная форма сменила знаменатель, но прогон над книгой, полной в обоих проходах, по-прежнему невидим до ready; канонному минору полосы НЕ наследовать «the fraction always reaches one» без оговорки. - **PD-396 — остаётся** (вопрос владельца по строке); замечено: счёт нерешённости теперь едет квитанцией двери (`signature`), `UnsignedBankTerms` так и мёртв — снятие поля не брал (не в §3). - Остальные 42 совпадения (PD-6…PD-397 по списку грепа) — **оставлены: совпадение по файлу, не по механизму** — пак их механизмов не трогал; полный список воспроизводится: `python3` -скриптом по `DEFECT_REGISTER.md` (колонка «Где» × список файлов описи). ### Чего в паке НЕТ (по §3.7 — пропуски подписаны) Читающая сторона банка (221/224/226 — на стопе провод банка ПУСТ, видно живьём в пробое, шаг 5) · sqlc · строка 198 · PD-375…PD-398 кроме PD-399 · воркер решений (синхронность ДЕРЖИТСЯ — замер, не рассуждение: живой вызов двери — доли секунды при потолке, подобранном движком под таймаут) · снятие обхода `--verify-bank` из пинга №21 (не в §3; `bank_released` остался в спавне и чтениях глав). ### Obstacle — что НЕ удалось и что НЕ проверено - **Живой пробой на ОБЛАЧНОМ провайдере не удался: в файле ключей деплоя нет ни одного живого ключа** (deepseek/zai/kimi/gemini/mistral/openai — все названы движком отсутствующими; grok — упёрся в конфиг аддитивного биллинга раньше ключей). Цепь пробита на `local`-заглушке — она доказывает ШОВ (дверь → глагол → файлы → resume → пере-сбор банка), но НЕ качество и НЕ поведение под латентностью настоящего провайдера. Строка 202 «книга насквозь по-настоящему» упирается теперь ровно в ключи. - **Таймаут-ветка двери (SIGTERM по бюджету) и класс 15 живьём не воспроизводились** — юнит-пины есть, живого файлового отказа не строил. - **`bank_corrections_enabled: false ⇒ 404` живьём не гонял** (нужен второй демон без движка) — юнит-пин `TestAnUnmountedCorrectionDoorAnswers404AndSaysSoInCapabilities`. - **Стенд-эффект на батарею**: живой юнит стенда оставил `tm-runs.slice` без контроллеров памяти — `TestARunIsBoundedByItsOwnCgroup` падал, пока слайс не сброшен (`systemctl --user stop tm-runs.slice`); это среда, не регрессия — на чистом слайсе зелёный. Приёмке знать при пере-прогоне. - **Пробные артефакты вне репо**: `~/tm-p9-work/` (стенд, логи батарей, fake-провайдер), `~/tm-p9-mut/` (копия с каноном + логи посадок). БД стенда `tmp9stand` на локальном PG 5432 — можно сносить. В КАТАЛОГЕ КНИГ стенда остались мои крафтовые книги — репо не касаются. - Сырые логи двух опровергателей не сохранены в зону — их отчёты абсорбированы сюда и в PD-400/401. ### Вопросы оркестратору 1. **Канонный минор полосы** (D39.160: полоса едет паком, механика версии — за тобой): деплой этой сдачи шлёт `progress.done/total` в сквозной семантике (chapter-passes работы ПРОГОНА) + новый член `progress.stage` (открытый словарь `drafting`/`editing`) — впереди объявленного 0.5.0 по прецеденту `Run.stop_requested` (`v0.go`, ⚠-коммент у `wireProgress`). Канону нужны: пере-описание `Progress` (снять «restarts from zero», НЕ наследовать «always reaches one» — PD-281), член `stage`. Кадр `progress` меняется тем же минором (`EventProgress` ссылается на ту же схему). 2. **PD-370** — закрытие строкой за лендингом (см. диспозиции). 3. **Провайдерские ключи деплоя** — нужен хотя бы один живой ключ, чтобы строка 202 прошла на настоящем провайдере; какие имена ключей движок ждёт — в логе пробоя. ### Аддендум приёмки (27.08, вечер) — блокер ревьюера по полосе: причина починена, лендить ли — слово владельца Ревьюер приёмки (`textmachine-29`) принёс блокер: прогон, стартовавший при `edit_wave = false` с переворотом флага ПОСЛЕ старта (движок объявляет форму волн первым progress-событием), делал всю купленную работу с полосой `0/N` навсегда — база снята флаг-зависимым предикатом момента старта и не пере-базируется. Мой пак знал механизм (текст миграции 00026 его называл) и вылечил только legacy-прогоны на буте, оставив генератор живым; строка `PD-401` его называла, но с заниженным весом и формулировкой «окно секунды». **Сделано по первому заказу оркестратора (до его поправки «жди слова» — работа уже была зелёной, откат по слову, не молча):** причина, не следствие — обе базы снимаются на ФИКСИРОВАННЫХ колонках (`chapters_before` — редакторская, `draft_before` — черновая, `StartRun` без CASE по флагу), пара «числитель+база» выбирается ЖИВЫМ флагом в момент чтения (`runDone`: `draftBar+editBar` против `draftOnlyBar`); миграция 00026 переписана (live-прогоны — обе базы на фиксированных колонках); пин ровно на прод-порядок переворота — `TestAFlagThatFlipsAfterStartDoesNotStrandTheBar` (флаг false ДО `StartRun`, переворот ПОСЛЕ, вся работа → 2/2). Сценарий блокера сходится: до переворота 0/C, после — `draftWork = 0` ⇒ total = C, редактура двигает 0→C, ready C/C. **Побочно вскрыто и починено (класс PD-1):** два старых пина `recut_test.go` пережили снос сегментной модели с ложными словами — `TestLiftingTheSigningStopRestartsTheBarFromZero` объявлял «Mutation caught: dropping the re-capture from the re-open», а ре-кэпчер снесён и тест зелёный; переименован в `TestLiftingTheSigningStopLeavesTheBarWhereItStood` с честным свойством и живым mutation-catch (спутать пары «числитель×база»); комментарий `TestTheBarAndItsBaselineCountTheSamePass` переписан под пары-по-колонкам. **PD-401 пере-формулирована и пере-взвешена** (info → minor, переезд в minor-секцию) по слову оркестратора: постоянная слепота платной работы, не транзиентный скачок; лечение в дереве названо в строке, статус open до решения владельца о составе лендинга. Регистр: 401 строка, 97 открытых (3/28/66), оба гейта чисты; полный `pgstore` зелёный (`go test ./internal/pgstore/ -count=1` → ok). Ось денег на нетривиальном состоянии (открытый холд + после расчёта) ревьюер исполнил на этом дереве сам — расхождений нет. ### Аддендум 2 (28.08, по слову владельца «техдолг в паке не держим») — PD-400 разобрана по половинам **Половина 1 (слово `run_in_flight` шире правды) — ЗАКРЫТА в дереве**, и лечение оказалось точнее, чем строка думала: под мьютексом и ПОСЛЕ проверки строки прогона класс 12 от глагола прогоном быть не может по построению (Start/Resume ждут тот же мьютекс, реконсилер рестартует только живые строки, которые проверка видит) — значит `run_in_flight` отвечается ТОЛЬКО из проверки собственной строки, а класс 12 глагола едет `503 service_unavailable` «занято, повтори позже» с ERROR-строкой оператору. Пин — обновлённый кейс вердикт-таблицы; `-race` по четырём задетым пакетам зелёный, линтер 0 issues. **Половина 2 (внутрипроцессный мьютекс) — граница v1, названная с условием и ценой** в строке и в шапке `bank.go`: сериализация сужается до пер-репличной при второй реплике; цена — холостая попытка и «повтори позже», не ложь и не деньги; лечение при второй реплике — арбитр в хранилище. Предложен перевод половины в «Принятый риск» словом лендинга. **`supervisor.go:35` проверен по существу — ЧЕСТЕН для своего дев-пути** (ключи там законно едут окружением/наследованием); дописана одна страховочная фраза «прод передаёт ключи аргументом `--keys-file`; копировать этот канал в прод-спавн — ловушка паритета, которую флаг и закрыл». Сдвинутые этой правкой якоря runs.go пере-нацелены, оба гейта регистра чисты. ## ПАК P9 — ЗАПИСКА-ПЛАН (сессия платформы, 27.08, промт `docs/PLATFORM_P9_SESSION_PROMPT.md`) План до правок, по §6 промта. Итоги и таблица комплектности — записью сдачи ниже по завершении. 1. **§3.3 Ключи движку.** Новый конфиг `TM_PLATFORM_ENGINE_KEYS_FILE` (абсолютный путь или отказ на буте, как `StateDir`) → `RunnerConfig.KeysFile` → `runs.Config` → `runner.TranslateArgs` получает `--keys-file` (флаг принимает ТОЛЬКО `translate` — `cmd/tmctl/invocation.go:151-157`). В окружение юнита ключи не кладутся. Пусто = флаг не передаётся (сегодняшнее поведение), с WARN на буте. 2. **§3.4 Полоса.** Форма: `done/total` пере-определяются как «проходы-главы через ОБЕ волны»: `total = 2×ceiling` при редакторе (`edit_wave`), иначе `ceiling`; `done = clamp(главы-с-черновиком − draft_before) + clamp(главы-с-последним-проходом − chapters_before)`, каждый clamp в `[0, ceiling]`. Монотонно по построению (счётчики юнитов только растут, базы фиксированы на старте), база и кап прогона сохранены (комментарий `readmodel.go:437-445` чтится). Миграция 00026: `runs.draft_before` (бэкфил = `chapters_before`); `StartRun` снимает ОБЕ базы, ветвление по `$3=verify_bank` уходит. Подпись «что делается сейчас» — новое поле `progress.stage` (`drafting`/`editing`, открытый словарь, клиент рисует фразу сам) в JSON и в кадре `progress`. ⚠ Канон 0.5.0 описывает `Progress` посегментно — канонный минор к смене едет с лендингом (D39.160: полоса ЗДЕСЬ, механика гейта версий — у оркестратора); прецедент поля впереди объявленной версии — `Run.stop_requested` (`v0.go:138`). 3. **§3.5 PD-399.** Снять `pending_decisions`/`complete` с `BankPage` и `EventBank`: `BankCounts`, `payload()`, `wireBankPage`, `listBank`; пин `reading_test.go:113` ПЕРЕПИСЫВАЕТСЯ на новый контракт (присутствие total/signed + ОТСУТСТВИЕ снятых полей) — правка с пином по заказу. 4. **§3.6 Дубль конвенции.** Движок публикует конверт `artifacts` (`bank_export` и др.) в `status --json` И `manifest --json` (`backend/internal/pipeline/status.go:110-134`, лендинг d1eb8a9). `ingest.Manifest` получает конверт; `refreshBank` берёт путь из ТОЛЬКО ЧТО прочитанного манифеста той же refresh-пачки; `runner.projectDB()` и парс `book.yaml` удаляются. Без фолбэка: старый движок без конверта = ошибка с именем причины, долг ретраится (порядок деплоя «движок первым» — норма зоны). 5. **§3.1+§3.2 Дверь.** Маршрут `POST /books/{bookId}/bank/corrections` в таблице `contractSurface`, монтирование по `Deps.Bank != nil`; `Capabilities.bank_corrections_enabled` = тот же факт; false ⇒ 404. Вызов СИНХРОННЫЙ: обработчик валидирует проводную форму (строгий декод, оба потолка ДО спавна: >1 МиБ → 413 через кап тела маршрута, >5000 и вся форма → 400), рендерит документ движка (`tm-bank-decisions-v1`, null-окна → 0), кладёт во временный файл под `StateDir`, зовёт `tmctl bank-apply` прямым чайлдом с бюджетом `RunBudget` (60 с — класс бюджета вызовов движка), декодирует отчёт v2 аллоулистом, отвечает квитанцией. Сериализация: пер-книжный мьютекс в `runs.Service`, его берут corrections И `Resume`/`Start` (та же гонка на спавне свежего прогона); проверка «жив прогон» — ПОД мьютексом (`ReadBookForRun.HasLiveRun` → 409 `run_in_flight`). Раскладка кодов: заказанное — 14→409 `bank_corrections_refused` (+`refusals[]` в `Problem`), 15→503 `bank_corrections_incomplete`, 12→409 `run_in_flight`, тело >1 МиБ→413. Моя половина, с доводом в отчёте: 13 (схема не мигрирована — оператор, транзиентно) → 503 `service_unavailable`; 19 (класс без номера — рассинхрон сборок) → 503 `service_unavailable`; 10/11 (сломан вызывающий/ деплой) → 500 `internal_error`; exit 5 и таймаут бюджета → 503 `service_unavailable`. Ни один не сливается в неразличимый 500: три ремеди («пере-реши»/«повтори то же»/«позови оператора») различимы по коду. `depth: edit_wave→refinement`; неизвестный depth движка НЕ форвардится (утечка имени волны) — 500 с ERROR-строкой. 6. **Самопроверка §4:** батарея с тремя гейтами на копии-с-каноном (скипы отдельным `-v`-грепом); живой пробой двери на стенде против настоящего движка (книга → банковый стоп → preview → правка → resume → следующий прогон видит правку; лог в отчёт); посадки мутаций — минимум по одной на дверь/ключи/прогресс, вердикт по дельте и топичности; опровергатели (2 агента): раскладка кодов и форма полосы. 7. **Не делаю:** читающая сторона банка (221/224/226) · sqlc · строка 198 · PD-375…PD-398 кроме PD-399 · воркер решений · снятие обхода `--verify-bank` из пинга №21 (не в составе §3; трогаю `bank_released` только в чтении полосы). ## ПАК P8-REVIEW ПРИНЯТ И ЗАЛЕНДЁН (оркестратор №19, 27.08) — ратификация D39.159 Пак принят целиком: четыре оси, 24 новые строки, 17 дописок, каталог воспроизведения. Тело приёмки — нота **D39.159**, здесь только то, чего в ноте нет, и что зоне нужно знать для следующего пака. **Что приёмка пере-мерила своим исполнением, а не приняла на слово.** · Батарея пере-прогнана с ТРЕМЯ гейтами на своей базе — 18 пакетов, EXIT=0, линтер 0 issues, скипов 0 (счёт снят отдельным `-v`). Ваши четыре числа воспроизвелись. · ⚠ **Движок для гейта живого рендера собран из HEAD, то есть уже с лендингом бэкенда `d1eb8a9`,** которого 24.08 ещё не было. Это сильнее вашего замера: шов пережил переписанную входную дверь. · `PD-379` подтверждён чтением: `pump` получает разрешённого пользователя и до конца соединения строку сессии не смотрит; маршрут — `internal/httpapi/v0.go:84`. Ваш артефакт честен вплоть до оговорки про 404 у `logout-all` на дев-профиле. · `PD-376` подтверждена СВОЕЙ посадкой: `min` → `max` в `internal/pgstore/runs.go:588`, копия с каноном по вашему же рецепту, чистая базовая линия EXIT=0/FAIL=0 — после мутации батарея ОСТАЛАСЬ зелёной, названный пин не упал. Спор по `PD-159` обоснован. **Диспозиции пяти ваших вопросов.** 1. **Статусы `PD-159`, `PD-293`, `PD-361` НЕ пере-открываю.** Описанный строкой дефект из кода ушёл — недостаёт не лекарства, а ПИНА, и пробел уже несёт своя открытая строка. Пере-открытие сказало бы «денежный баг вернулся», что ложно, и посчитало бы один пробел дважды. **Токен `ОСПОРЕНО(PD-N)` ратифицирован** как стоячий инструмент — с условием двусторонней ссылки, которое вы уже выполнили (проверил все три пары). Инструмент хороший: он держит спор грепаемым вместо прозы. 2. **Обе правки ратифицированы** — норма §3 п.3 и эррата §13. Отдельно одобряю дисциплину эрраты: абзацы политики оставлены до закрытия `PD-379`. Переписывать обоснование раньше, чем закрыт дефект, значило бы задним числом объявить нормой то, что дефектом и признано. 3. **Галочку `ASVS 7.4.1` пометил сам** — эрратой в БАННЕРЕ архива, а не правкой строки. ⚠ Файл `docs/archive/platform-PROGRESS-P0-P3.md` — В ВАШЕЙ зоне, не в чужой; чужая для вас — `docs/` корня. При архиве правку делает лендер, поэтому сделал я, но перестраховка стоила вам вопроса. 4. **Норма принята и вписана — `ENGINEERING_STANDARDS` §3 п.8.** Машинная половина заведена строкой бэклога 225 в `docs/PROGRESS.md`. ⚠ **Ваш гейт замерен ПРЕЖДЕ постройки и требует ПОЛНЫХ путей:** по именам файлов он даёт 22 совпадения, почти все ложные (`main.go`, `runner.go`, `config.go` есть в обеих зонах), по полным путям — 2, и оба настоящие. Он окупился до постройки: нашёл, что якорь `PD-157` (`backend/internal/config/book.go:250`) убит МОИМ лендингом `d1eb8a9` — требование уехало на `:321`. Пере-нацелил и назвал причину. Симметричное правило дописано в норму: якорь, убитый переездом, чинит тот, чей переезд его убил. 5. **`PD-398` — ОТКЛОНЁН замером, строка пере-формулирована.** Предложенное `n != shape` краснит СЕМЬ законных строк: колонки в `counts.py` читаются С КОНЦА НАМЕРЕННО, и комментарий на `:132` приводит ровно этот случай. Числа регистра из-за избытка не врут — `PD-99` и `PD-197` разбираются верно. Настоящий остаточный риск другой: избыток в ХВОСТОВОЙ ячейке. ⚠ Я воспроизвёл его на себе, пока правил эту же строку: вписал вертикальную черту в ячейку статуса, и `open` мгновенно стал «иное», а счёт открытых — 95 вместо 96. Экранирование `\|` парсеру НЕ помогает, он режет по сырому символу. **Что осталось вам и чего я НЕ делал.** Строки пака не чинил ни одной — это работа кодового пака. Ваших «чего не проверял» я тоже не закрывал: сырые логи 42 агентских посадок, `-race` у выживших, «до 17 раз» в `PD-377`, живая проба `PD-96`, экономика `PD-215`, кардинальность лейблов. Они остаются названными пробелами, а не тихими. **Об ошибках, которые вы назвали сами** (пять, от овер-атрибуции `PLATFORM_DIRECTION` до неполной описи): называть их — правильно, и опись, недобравшая три файла из пяти, действительно стоила бы лендеру правки НОРМЫ. Продолжайте так же. ## ПИНГ №21 от оркестратора (27.08, D39.158) — обход флага банка стал лишним, но снимать его ТОЛЬКО после движка Движок теперь сам исполняет модель D39.144: стоп банка срабатывает только на кластере, которого ни один прежний стоп не предъявлял, а возобновление БЕЗ решений продолжает прогон. Персистентная память живёт в движке (схема хранилища v16). **Что у вас становится лишним:** снятие `--verify-bank` при возобновлении, особый случай в спавне и персистентная колонка «банк отпущен» — их работа переехала в движок, где ей место по п.1 закона шва. ⚠ **Порядок обязателен и односторонний:** сначала лендинг движка (сделан) и `tmctl migrate` по каждой книге, и только ПОТОМ снятие обхода. Обратный порядок — и каждая книга с поднятым флажком перестаёт писать авто-банк ровно в том режиме, ради которого флажок существует. Порядок деплоя «движок первым» это обеспечивает. **Сверх того, к сведению:** заведён класс полосы `write_incomplete` = **exit 15** (и «не легло ничего», и «легла половина» — ветвление одинаковое: условие хоста, не книги; повтор безопасен). Отчёт двери — `tm-bank-decisions-report-v2`. Полоса получила два яруса и гардрейл «разрушительное действие ключуется на КЛАССЕ, никогда на принадлежности полосе» — у вас он истинен де-факто, правки не требует. Машинная таблица стопа `.bank-stop.json` СНЕСЕНА (читателя не было ни в одной зоне) — если её называют ваши доки, упоминания протухли. **Строки бэклога, которые вас касаются:** 221 (джойн «предложено × решено × нерешено» живёт только в движке — экран подписи заставит вас пере-реализовать его закон, чего п.6 не разрешает; лечение — публикация движком, когда экран закажут) · 224 (банк-экспорт на стопе пуст). ## Текущее состояние **⛔ ПИНГ ОРКЕСТРАТОРА №19 — 23.08, ЗОНЕ ПЛАТФОРМЫ. Три находки, все сверены моими командами; рукой в код не лезу.** 1. **Пере-нарезка книги сносит ВСЕ решения юнитов и пересчитывает прогресс из пустоты.** `internal/pgstore/readmodel.go:127-137`=`a re-cut drops the resolutions of the previous cut` — это не догадка, там собственный комментарий кода. Строку в ваш регистр я не заводил: решать вам, намеренная это семантика пере-разреза или дефект, и что делать со счётчиками, которые после этого показывают ноль сделанного на книге, где работа была. 2. **⛔ БЛОКЕР ЖИВОГО ПРОГОНА: движок на SaaS не получает провайдерских ключей** — строка **211** единого бэклога, тело там. Ваша половина: путь к файлу ключей едет из конфига платформы в аргументы движка, а дев-супервизор переводится на тот же механизм. Сейчас пути РАЗНЫЕ — `internal/ingest/supervisor.go` (до-паковые строки 35-36 и 65-67 — «Provider keys reach the engine through it»; комментарий переписан лендингом P9, якорь исторический) наследует окружение платформы, а прод-спавн ставит юниту ровно один элемент (`internal/runs/spawn.go`, до-паковая строка 168 `Env: engineEnv(…)`; с лендингом P9 ключи едут аргументом `--keys-file`, якорь исторический). Поэтому стенд зелёный, а прод голодает, и ни один тест упасть не мог. Пока два пути кормят движок по-разному, следующий такой блокер снова пройдёт всю батарею. 3. **Дубль движковой конвенции у вас.** `internal/runner/artifacts.go` (до-паковые строки 65-95 — «neither project_db nor book_id»; `projectDB` снесён лендингом P9, файл ужался, якорь исторический) сам вычисляет путь БД книги, повторяя `backend/internal/config/book.go` (до-паковые строки 163-167 — `b.ProjectDB = filepath.Join(…)`; конвенция ушла в конверт манифеста движковой половиной, якорь исторический); смена дефолта в движке тихо уведёт ваше чтение банка на несуществующий путь. Строка **213**. Фикс аддитивный и без ломки: движок отдаёт путь артефакта, вы выкидываете свой `projectDB()`. ⚠ Пере-именование банк-экспорта в фикс-имя — ЛОМАЮЩЕЕ, ему место в окне строки 161, не здесь. 4. **Поле, которое декодируется и не читается.** `ingest.StatusReport.UnsignedBankTerms` (`internal/ingest/resync.go:36`=`UnsignedBankTerms`) разбирается из ответа движка и не используется НИ ОДНОЙ строкой продакшн-кода зоны. Либо потребитель потерян при спиле пер-термного пути, либо поле лишнее — решать вам; я называю факт, потому что мёртвое поле в структуре шва читается как контракт. *(Промт `P8-REVIEW` жив и ждёт слова владельца. ⚠ Правка к этой же записи, 23.08: я объявил в ней ДВА дефекта промта — реальным оказался ОДИН и он ПОЧИНЕН (миграция `00019` названа денежной, хотя это `00019_read_model_debt.sql`, долг read-модели; ось 1 по ней ушла бы не туда). ⚠⚠ **Второй дефект РЕАЛЕН, и моё прежнее «не воспроизвёлся» было ЛОЖЬЮ — снимаю её здесь же.** Я написал зоне «грепа `13 изменённых|6 новых|2166` в промте ноль». Греп СОВПАЛ: строка 42 промта несёт «в дереве живёт НЕЗАКОММИЧЕННАЯ работа полигона (13 изменённых файлов и 6 новых под `eval/`)». Я обрезал вывод грепа по ширине, увидел начало строки и принял совпадение за отсутствие. Счёт при этом действительно протух: на 23.08 в дереве **14 изменённых и 5 неотслеженных**. Дефект ПОЧИНЕН тем же заходом — числа из промта убраны, он теперь ссылается на единственный носитель счёта, а не держит свою копию. Записываю, потому что обещание зоне, исполненное наполовину без объяснения, читается как невыполненное.)* - **ПАК P8-REVIEW ОТРАБОТАН (24.08) — четыре оси прочитаны, кода не тронуто.** **Что уезжает на лендинг (опись пере-снята командой, лендить по ней):** `git status --short -- platform/` → `M docs/DEFECT_REGISTER.md` · `M docs/platform-PROGRESS.md` · `M docs/ENGINEERING_STANDARDS.md` · `M docs/STACK_DECISIONS.md` · `M docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md` · `?? docs/p8-review/`. ⚠ **Три последних — не находки, а правки, и две из них требуют вашего слова:** в `ENGINEERING_STANDARDS` §3 вставлен НОВЫЙ пункт 3 (рецепт копии с каноном и правило «дельта плюс топичность») — это правка НОРМЫ зоны; в `STACK_DECISIONS` §13 вписана эррата к обещанию «мгновенный отзыв», которое `PD-379` опровергает; в промте пака исправлен рецепт копии. `.go` в репозитории не тронуты (`git diff -- platform/ ':!platform/docs'` пуст), индекс чист. **Числа сдачи (сняты ПОСЛЕ последней правки регистра):** · `python3 docs/scripts/counts.py --check` от корня → **398 строк, открытых 96** (3 major, 27 minor, 66 info); было 374 и 72 (1/16/55). **24 новые строки** `PD-375`…`PD-398`, **17 ⚠-дописок** к чужим. Чужие статусы и веса не сдвинуты: вся дельта открытых — мои новые строки. · `make check` с тремя условиями → **18 пакетов, EXIT=0, линтер 0 issues** (`docs/p8-review/battery-final.log`). Скипы лог не считает — секция «did NOT run» печатается только при найденном `--- SKIP`, — поэтому счёт снят отдельно: `go test ./... -count=1 -v` с грепом по `--- SKIP` → **0**. Это и есть недостающий замер к `PD-374`: на этом хосте выполнимы все три условия. · `counts.py --lint` → по `platform/docs` один якорь, и он ЧУЖОЙ и транзитивный: `platform-PROGRESS.md:27` целит в `backend/internal/config/book.go:163-167`, против HEAD там строка 164 — верно, — а красным его делает незакоммиченная работа соседней бэкенд-сессии, двигающая цель на 188. **Правку этого якоря я сделал и ОТКАТИЛ: не пере-нацеливать.** Прибить его к транзитному положению значит сломать в момент лендинга или отката соседней работы. · Посадок мутаций: мои **16** (все воспроизводимы — `plant.py` знает поимённо, логи рядом), 7 поймано, 9 выжило; агентских **42** по их таблицам (`docs/p8-review/agent-mutations.md`), 21/21. Уникальных переживших инвариантов 15, каждый несёт строку. **Ось → чем закрыта → находки.** Предмет каждой оси взят из промта целиком; ниже только отклонения. 1. **Деньги и леджер.** Обязательная норма исполнена дважды и на НЕтривиальном состоянии — `docs/p8-review/reconcile-with-open-hold.txt` (8 строк леджера, ОТКРЫТЫЙ холд $0.06 в момент замера, оба пути сошлись); независимо то же сделал агент оси на своём стенде. Вторая половина нормы (леджер = НИЖНЯЯ граница) проверена отдельно: расход СВЕРХ холда каппится и живёт только текстом в `note`, который не читает ни один путь кода — `internal/pgstore/credits.go:225` плюс ноль `SELECT` по колонке. Это проектное решение, строкой не заведено. **Находки: `PD-375` `PD-376` `PD-377` `PD-378` `PD-394` `PD-397`.** 2. **Вход, сессии, CSRF** — ось, которой не касался ни один пак. Живые пробы на демоне со СВОИМИ короткими потолками, матрица CSRF по маршрутам, посадки, сверка с ASVS 5.0 и RFC. **Находки: `PD-379` (единственный `vuln` пака, major) `PD-380` `PD-381` `PD-382` `PD-383`.** 3. **Очередь и джобы.** Смотрел не сам блокер `PD-169`, а его сиблингов И его лечение. Пробы на РЕАЛЬНЫХ функциях с реальным Postgres. ⚠ Пять якорей промта по этой оси сверены и живы дословно. **Находки: `PD-384` `PD-385` (major) `PD-386` `PD-387` `PD-388`.** 4. **Метрики — исполнением.** Снимок в покое → вызванный отказ → снимок → дифф; все три в `docs/p8-review/axis4-metrics/`. **Ответ на вопрос оси «что оператор понял бы по этой дельте»: почти ничего.** Переход в `stalled` по одной `/metrics` виден; отказ свипа — нет (меняется только гистограмма длительности); гейджи при отказе чтения замирают без признака устаревания; инстанс, у которого чтение не удалось ни разу, отдаёт нули как здоровье. Случаи, где `run abandon` ОТКАЖЕТ, оператор различает — колонка SPENT печатает «?» ровно при пустой базовой линии, биекция; это единственная находка пака, **опровергнутая** рефутером по всем четырём состояниям. **Находки: `PD-389` `PD-390` `PD-391` `PD-392` `PD-393`.** Сверх осей — из сверки регистра с деревом: `PD-396` (мёртвые поля шва) и `PD-398` (гейт формы регистра ловит недостачу ячеек, но не избыток). **Таблица посадок — мои, вторым рубежом поверх агентских** (`docs/p8-review/plant.py`, логи `mutations.log` · `mutations-full.log` · `mutations-round2.log`): | # | ось | что посажено | какой пин обязан был упасть | упал | |---|---|---|---|---| | M1 | деньги | снята пере-проверка «попытка уже заспавнена» в `ReleaseUnspawned` | `TestAStaleSnapshotDoesNotGiveBackTheHoldOfAnAttemptThatSpent` | ✅ | | M2 | деньги | снят кламп по `SpendBound` в `settle` | `TestADeferredSettlementIsNotChargedForTheNextRunOfTheSameBook` | ✅ | | M3 | деньги | `holdTx` отвечает `nil` вместо `ErrDuplicateHold` | `TestAHoldThatDebitedNothingIsRefused` | ✅ | | M4 | деньги | снят гард `spent < 0` в `Settle` | носителя нет | ❌ → `PD-394` | | M5 | деньги | снят кламп-в-ноль в `attemptSpend` | `TestAMeterThatWentBackwardsSettlesAtNothingAndSaysSo` | ✅ | | M6 | вход | `Secure: !c.Insecure` → `Secure: false` | `TestLoginCompletesAndCreatesOurOwnSession` | ✅ | | M7 | очередь | `phaseBudget` отдаёт весь проход вместо половины | два пина фаз | ✅ | | M8 | метрики | снят `m.stalledRuns.Set(...)` | носителя нет | ❌ → `PD-389` | | M9 | метрики | снят инкремент `sweepUnfinished` | `TestTheRunnersStateIsExposedWithItsUnits` | ✅ | | M10 | метрики | снят `m.abandonedSurfaces.Set(...)` | носителя нет | ❌ → `PD-389` | | M11 | деньги | `SpendBound`: `min` → `max` | носителя нет | ❌ → `PD-376` | | M12 | вход | абсолютный потолок сессии умножен на 100 в `CreateSession` | носителя нет | ❌ → `PD-380` | | M13 | вход | `ClearSession` выдаёт TTL `time.Hour` вместо `-time.Second` | носителя нет | ❌ → `PD-382` | | M14 | вход | в `set` ветка `ttl < 0` даёт `maxAge = 3600` | носителя нет | ❌ → `PD-382` | | M15 | вход | срок хранения журнала входов 180 суток → 180 ЛЕТ | носителя нет | ❌ → `PD-383` | | M16 | вход | предикат свипа журнала обезврежен | носителя нет | ❌ → `PD-383` | Пол по каждой оси выполнен — колонка «ось». Все девять выживших пере-проверены на ПОЛНОЙ батарее против чистой базовой линии ТОЙ ЖЕ копии, не по exit-коду. ⚠ **Правило вердикта, которое пере-ранящему знать обязательно:** судить по ДЕЛЬТЕ против чистой копии И по ТОПИЧНОСТИ упавшего теста, а не по цвету батареи. Цена забывания оплачена здесь дважды: известный флейк `PD-369` дал ложное «пойман», а первая редакция `mutations-full.log` печатала `RED` там, где мутация выжила. Правило записано в `ENGINEERING_STANDARDS` §3 п.3, `PD-382` и `PD-395`. **Реестр против дерева — 17 ⚠-дописок.** Строка `open`, чьё лекарство уже в дереве, отправляет следующий пак чинить построенное. Предлагаю ЗАКРЫТЬ: `PD-23`, `PD-203`, `PD-166`. Сузить, но НЕ закрывать: `PD-162`, `PD-368`, `PD-374`. Пере-формулировать: `PD-6`, `PD-157`, `PD-168`, `PD-90`, `PD-101`. Свести с условием, названным в дописке: `PD-251`↔`PD-42`, `PD-250`↔`PD-179`. Помечены токеном `ОСПОРЕНО(PD-N)` — статусы паком НЕ менялись: `PD-159`, `PD-293`, `PD-361`. Конвенция токена записана в шапку регистра. **Аддендумы владельца по ходу (§5 промта) — три, результат:** 1. «Архитектурно чистое решение по `PD-395`?» → строка ПЕРЕПИСАНА, диспозиция сменилась с «править гейт» на «править рецепт», рецепт исправлен и проверен исполнением, а лечение переехало из эфемерного носителя (промт архивируется при лендинге) в `ENGINEERING_STANDARDS` §3. 2. «Посоветуйся со старшим; веди через существующего агента» → два захода старшей модели, оба абсорбированы; дальше вёл сообщением, а не новым агентом. 3. «Доводи до конца» → рефутер по восьми утверждениям сверки реестра (последний рубеж без адверсариальной проверки) и редакторский аудит сдачи; оба нашли дефекты, оба закрыты. ⚠ **Мои ошибки, которые нашли не я, и они того же класса, который пак ловил.** (а) Сослался на `PLATFORM_DIRECTION` §3 как на ратифицированное направление по `oapi-codegen`, не сверив, что оно ПЕРЕ-ПОДПИСАНО `D39.132` в «кандидат» и P7 решил не брать. (б) Дописка к `PD-166` описывала механизм, недостижимый в сегодняшнем коде. (в) Сужения четырёх строк оси 3–4 оказались КРУГОВЫМИ — каждое опиралось на поверхность, несостоятельность которой доказывает соседняя строка того же пака; из-за этого `PD-385` стояла minor. (г) `PD-379` несла вес major, а лежала в секции minor. (д) Опись изменённого называла два файла из пяти — лендер недобрал бы правку НОРМЫ. Всё исправлено; называю, потому что необъявленная ошибка автора — это находка, которой нет. **Obstacle.** · Чужой демон `tmplatformd` pid 211037 (порты 8080/9464, база `tmstand`) — не трогал; пак изолирован своими портами 8099 и 8101–8114 и базами `tmp8rev_*`. Соседняя бэкенд-сессия живёт в дереве (18 файлов) — отсюда транзитивный якорь выше. · Первый запуск четырёх осей умер на середине (обрыв сессии плюс `Connection lost` у одного агента). Копии сверены `diff -rq`, базы пере-созданы, всё пере-запущено с нуля. · **Чего НЕ проверял.** Сырые логи 42 агентских посадок не сохранены — их вердикты пере-ранить нельзя, только пере-посадить по описанию; из 21 «пойман» 19 называют конкретный тест, 2 нет. Пере-проверка выживших шла `go test ./...` БЕЗ `-race` (для класса «пина нет» вердикта не меняет, но сказано). Не пере-мерены мной: «до 17 раз» в `PD-377`, живая проба preflight из `PD-96`, экономика петли `PD-215`, кардинальность лейблов под нагрузкой. Числа пробы в дописке `PD-162` сняты рефутером, лог в артефакты не попал. Клейм «$0.15 против $0.03» СНЯТ — лога не осталось, механизм на его месте доказан грепом. **Вопросы приёмке.** 1. `PD-159`, `PD-293`, `PD-361` стоят `fixed`, а их пины доказывают меньше. Статусы не менял — пере-открыть это одна правка, и она ваша; улика и живые пробелы вынесены новыми строками. 2. Правка НОРМЫ (`ENGINEERING_STANDARDS` §3 п.3) и эррата к `STACK_DECISIONS` §13 сделаны мной в своей зоне, но обе меняют объявленное — нужна ваша ратификация или откат. 3. Снятие галочки `ASVS 5.0 7.4.1` в `docs/archive/platform-PROGRESS-P0-P3.md:572` — там она стоит выполненной словами «отзыв прекращает использование», что `PD-379` опровергает. Это ЧУЖАЯ зона. 4. Класс «open, а лекарство построено» стоит зоне ~10% открытых строк. Предложение, оба этажа вне права читающего пака: норма в промты кодовых паков («построил механизм → грепни открытые строки по именам своих файлов, каждое совпадение — диспозиция в отчёте») и, пингом в `docs/`, пересечение застейдженных путей с якорями открытых строк. 5. `PD-398`: `malformed()` в `counts.py` ловит только недостачу ячеек — однострочная правка в вашей зоне. - **P8-FIX ПРИНЯТ И ЗАЛЕНДЕН оркестратором №18 (22.08).** Тело приёмки — ратифицированная нота **D39.154** (`docs/architecture/05-decisions-log.md`), здесь не пересказывается. Зоне важно ровно следующее, и этого в ноте нет: - **Пинг зоны №1 ЗАКРЫТ:** якорь `05-decisions-log.md:542` → `internal/pgstore/perf_test.go:13` жив, токен «96% of the page» на месте — правку комментария бенчмарка приёмка приняла. - **Правку зоной ЧУЖОГО документа** (пере-нацеливание двух моих якорей в промте читающего пака после переезда констант) **утверждаю и не откатываю**: якоря умерли по вине пака, пак их починил и раскрыл это сам, вместо того чтобы обойти красный гейт. Это ровно то поведение, которого норма и требует, — так и делайте дальше. - **«Скипов 0» у меня НЕ воспроизвелось: три скипа.** Не ваша регрессия и не ошибка отчёта — `systemdOrSkip` гейтит три теста `internal/runner` достижимостью пользовательского менеджера systemd, а на моём хосте `/run/user/1000` нет. Незакрытым осталось то, что рецепт и критерий приёмки объявляли у батареи ДВА гейта, а их три: `PD-374`, оба носителя поправлены 22.08. - **Мои одиннадцать посадок мутаций вне вашего списка:** убито восемь, выжили три — `StalledAfter` законно (носитель один), `maxAttempts` и `truncateReason` дали `PD-373`/`PD-372`. Плюс находка вне карты пака `PD-371`. ⚠ Две первых редакции МОИХ посадок дали ложное «выжила» — гонял не тот пакет; пере-прогнано. Говорю, потому что необъявленная ошибка харнесса приёмки — находка, которой нет. - **Числа сдачи в вашем отчёте держатся**, кроме одного: «отпечатков миграций 41» — их 25 (`grep -c '^[0-9a-f]\{64\}' migrations.sha256`); число было названо без команды. Регистр после приёмки — **374 строки, открытых 72** (1 major, 16 minor, 55 info). - **Открытым уезжает `PD-370`** (контрактная половина) — не работа зоны, закрывать её здесь было бы подгонкой под критерий приёмки. Согласен с вашей диспозицией. ⚠ **Порядок паков вышел ОБРАТНЫМ очереди:** промт P8-FIX требовал запуска ПОСЛЕ читающего пака, запущен был раньше. В плюс — названная цена перестановки («блокер живёт всё время читающего пака») НЕ заплачена. В минус — §4.7 релея пуст, и читающий пак пойдёт по только что переписанному коду; его промт про это предупреждён баннером. - **Эра пака P8-FIX (21–22.08) — В АРХИВЕ.** Отчёт пака, обе волны ревью, спил пер-термной подписи, инвентарь каналов и obstacle — [archive/platform-PROGRESS-P8.md](archive/platform-PROGRESS-P8.md). Ратификация — D39.154, лендинг `31f1f82`. Живое из этой эры: четыре открытые строки регистра (`PD-370` мажор — контрактная половина · `PD-371` · `PD-372` · `PD-373`/`PD-374`), инвентарь каналов шва в `STACK_DECISIONS.md`, граница sqlc в `BACKLOG.md` П-19. - **Записи акта 5 пака P7, остававшиеся в живом журнале, — ДОСЛАНЫ в [archive/platform-PROGRESS-P7.md](archive/platform-PROGRESS-P7.md)** 22.08: заголовок «эра P7 в архиве» стоял, а тела лежали здесь. - **Запись оркестратора, 15.08 — P6 + ДОФИКС ПРИНЯТЫ И ЗАЛЕНДЕНЫ (D39.132).** Приёмка двумя раундами: панель 5 адверсариальных верификаторов по P6 (две линзы — другой моделью; исполнением, включая живой демон и живой PG) → фикс-лист ФП-1…ФП-8 → дофикс → финальная верификация исполнением: батарея `make check` под `-race` с живым PG и ОБОИМИ гейтами пере-прогнана оркестратором — EXIT=0, 17 пакетов, скипов 0; тестов **416** (`grep -rh '^func Test' platform --include='*_test.go' | wc -l`; HEAD = 359); деньги пере-считаны из сырого леджера стендовой БД двумя путями (сошлись до цента); живая проба рендера пере-снята своим tmctl из HEAD; 2 собственные посадки приёмки на дофикс — обе пойманы (`TestAFlaggedBookStillAnswersAboutItsMoney` · `TestEachOfTheThreeBlockersAloneKeepsABookOutOfTheMigrationList` — вторая до дофикса переживала всю батарею). **Исправлено оркестратором при лендинге:** откачено переименование закрытой строки PD-198 → PD-199 и удалена заведённая под PD-198 строка-дубль с ложным обоснованием (регистр 246 → 245 слиянием; «ID стабилен навсегда»); PD-200 перенесена в «Закрытые — эра P6», PD-180 — в «Открытые — info». Ратификации и диспозиция вопроса PD-241 — тело D39.132. - **ДОФИКС P6 ЗАКРЫТ — дерево передаётся на лендинг одним пакетом с P6 (15.08).** Фикс-лист приёмки ФП-1…ФП-8, раздел «Дофикс P6» ниже. Ни одна находка не опровергнута, но две подтвердились не так, как их описала приёмка. Самая тяжёлая — денежная и на ОБЫЧНОМ пути: движок отвечает exit 2 из `status --json` про любую книгу с помеченной единицей, а платформа читала это как «движок не ответил», из-за чего расчёт такого прогона откладывался вечно и следующий прогон книги не стартовал. Плюс аддендум владельца 15.08: `day_usd` убран из ПРИМЕРА шаблона книги. Батарея под `-race` с обоими гейтами: EXIT=0, 17 пакетов, скипов 0, линтер 0 issues, `make vuln` чист, **359 (HEAD) → 416** тестовых функций — счёт командами в разделе. Посадки 15 из 15. - **P6 ПОСТРОЕН — дерево передаётся на лендинг (14.08).** Потребительская половина шва эмиттера (П-15) · интейк формы Б (П-14) · дев-стенд с сидом и дев-входом (П-16) · порядок апгрейда движка против деадлока v15 · попутные PD · гигиена зонных доков. **PD-113 закрыт — открытых major в регистре НЕТ.** Батарея на стенде `~/.local/pgsql` (порт 55433) под `-race`: EXIT=0, 17 пакетов, линтер 0 issues, скипов 0, `make vuln` чист, тестов зоны **359 → 401** (числа P6; счёт — команды в разделе «Дофикс P6», прежний базис «396 → 403» не воспроизводился, PD-234). Посадки: 20 из 20 в первом круге (три переписаны после разбора переживших) + 6 из 6 на находки адверсариальной панели, одна названа НЕПИНЯЕМОЙ по построению. Живые пробы на настоящем `tmctl` и на фейке движка с правилом потолка — разделы ниже. ⚠ **Три вопроса на ратификацию оркестратору** (подробности в разделе P6): внутренняя причина `daily_ceiling` НЕ проецируется на провод (контракт знает одно значение — PD-199, спек-правка за S4) · направление `PLATFORM_DIRECTION` §3 предложено ПЕРЕ-ПОДПИСАТЬ (oapi-codegen и sqlc не исполнены, срок «до первого хендлера» пройден) · батарея получила ВТОРОЙ гейт окружения (`TM_PLATFORM_TEST_ENGINE_BIN` + `_BOOK_TEMPLATE`), без него один тест честно скипается. - **P5 ПОСТРОЕН и ДОФИКШЕН по приёмке — дерево передаётся на лендинг (11.08).** *(→ принят в три раунда и заленден D39.130, `69d485a` — запись оркестратора 14.08 ниже.)* `POST /v0/books` потоково с пер-маршрутным потолком тела и своим дедлайном чтения · статусы `uploading → parsing → not_started | rejected` получили писателей, разбор — $0-команда движка `tmctl manifest` · `POST /v0/runs/{id}/stop|resume` с намерением стопа в Postgres ДО сигнала · метрики Prometheus на отдельном слушателе · печать эффективной конфигурации. - **Приёмка 11.08 (8 находок) → дофикс → кросс-семейное ревью дофикса (5 находок) → ре-чек оркестратора (3 находки, 2 high) → ТРЕТИЙ РАУНД ЗАКРЫТ (14.08)**: разделы «Третий раунд», «Дофикс-2» и «Дофикс P5» ниже, движение — записками-планами. Батарея на стенде `~/.local/pgsql` (порт 55433) под `-race`: EXIT=0, 16 пакетов, линтер 0 issues, скипов 0, `make vuln` чист, тестов **261 → 359**. Посадки: 10/10 · 6/6 · 6/6 (две пережившие за все раунды — оба раза виноват был слепой пин, переписан пин, не мутация); списки — в журнале. ⚠ **Тулчейн 1.26.6 НА РАТИФИКАЦИЮ** *(→ ратифицирован D39.130)*, и вчерашний клейм про него был ЛОЖЕН: гейт принимал 1.26.5 (регекс вместо сравнения). Теперь floor держат два механизма — `make version-check` сравнивает версии, `go.mod` несёт `toolchain go1.26.6`, который читает всякая сборка; при `GOTOOLCHAIN=auto` хост скачает нужный тулчейн, при `=local` остановится. Само сравнение запинено (`internal/gates`). **ВОПРОС на ратификацию — кто пишет `book.yaml` при интейке**: половина П-9 стоит на нём, раздел «Развилка» ниже. *(→ решено D39.130: бета = форма Б, стройка = П-14; движковая форма В = строка 170 единого.)* - **Запись оркестратора, 14.08 — P5 ПРИНЯТ И ЗАЛЕНДЕН (`69d485a`, D39.130).** Финальная верификация исполнением: батарея EXIT=0 на том же стенде · **гейт версий живьём отказывает 1.26.5 и `go1.27rc1`, принимает 1.26.6 и 1.26.10** (sort -V честный) · `toolchain go1.26.6` в go.mod · сентинел `.tmplatform-books` на месте · `revision+1` остался ровно у двух заявленных прогресс-писателей синка (мотивированное исключение ратифицировано) · тестов **359**, регистр **198**, удалённых имён тестов против HEAD нет. **Ратифицировано (D39.130):** Go-floor 1.26.6 с toolchain-директивой (цена офлайн-хоста с `=local` названа и принята) · `book.yaml` при интейке — форма **Б** (рендер из деплой-шаблона; интерпретация D39.110 §2b с аудит-следом, стройка — следующим касанием зоны), форма **В** (`tmctl init`) — строка 170 единого бэклога движку · контракт-диспозиции PD-172/173/174/180 — спек-правкой 0.2.3 задачей S4-промта · PD-196 → дописка строки 165 (различимость классов отказа `manifest`). - **Пинг оркестратора, 14.08 (второй) — ЭМИТТЕР ДВИЖКА ЗАЛЕНДЕН (D39.131, `9cfe080`): `events.jsonl` начнёт появляться в каталогах книг.** Словарь = ваш `events.go` с диффом движка (принят приёмкой): `StreamVersion` **1.1** · `Ceiling.Scope: book|day` · `Finished.outcome += ceiling|stopped` · `eta_seconds` = темп ТЕКУЩЕГО прогона · `unit_done.unit` = лидерный `first_chunk_idx`. Ваша половина — **П-15** зонного бэклога (маппинг exit 4/5/10–19 · `unit_done` присваиванием · scope · dev-супервизор · порядок деплоя против деадлока v15 — строка 174 единого, РЕШИТЬ ДО деплоя нового бинаря). PD-113/PD-196 в регистре остаются open до П-15 — движковая половина построена, потребительская нет. - **Пинг оркестратора, 14.08 — гигиена зонных доков по аудит-свипу корпуса, закрыть следующим касанием зоны (вместе с П-14):** `README.md:85` «тулчейн ≥1.26.5» → 1.26.6 c toolchain-директивой · `README.md:87` «River не подключена — П-3» и `STACK_DECISIONS.md:23` «River в go.mod НЕ добавлен» — противоречат go.mod и построенной P4-очереди · `README.md:47-48` вопрос book.yaml → решён D39.130 (форма Б) · `PLATFORM_DIRECTION.md`: oapi-codegen «взять — доказано» против `ENGINEERING_STANDARDS:59` «кандидат при P1» — оба не исполнены (ручки P4/P5 рукописные), sqlc «до первого хендлера» не случился (PD-44 открыт), «River в go.mod не заводить» отстал — направление пере-подписать честно · `DEFECT_REGISTER.md`: PD-172/173/174/180 не несут диспозицию D39.130 (спек-правка 0.2.3 задачей S4) · открытые PD-180/185/196 живут в секциях «Закрытые…» · PD-178 стоит после PD-191 (порядок номеров) · PD-99 (fixed кодом) лежит в «Закрытых ратификацией» · путь `docs/scripts/counts.py` в шапке регистра исполним только от корня репо · веса `high`/`medium-high` секций дофиксов против объявленных major/minor/info · `STACK_DECISIONS.md:17` называет гейт «make tools-check», хотя `version-check` отделён (FP5-9) · П-1/П-3 зонного бэклога не знают построенного P4 (вес «Ф3, после контракта» отстал) · хвост (г) третьего раунда — перечислительность мета-пина systemd-гейта — носителя не имеет: завести PD-строку или закрыть явно. - **P4 «раннер» + дофикс + V2 ПРИНЯТЫ и ЗАЛЕНДЕНЫ — D39.123, код `d29e30c`** (ревью-шапка ниже): транзиентный systemd-юнит на прогон · очередь River с холдом-до-спавна в одной транзакции · реконсилятор с базовой линией расчёта · тейлер `events.jsonl` с карантином проекции · пять ручек `/v0` контракта **0.2.1** · дев-интейк. Батарея с живым PG 18.4 под `-race` зелёная, скипов 0, тестов зоны **261**. Формула аргумента потолка — `committed + прирост` (PD-158, ратифицирована ПОПРАВКОЙ к пингу оркестратора). - **Регистр — 245 строк** (`python3 docs/scripts/counts.py` от корня репо; было 246 — лендинг слил строку-дубль PD-198, испр. оркестратором №16 15.08): 178 закрыто · 1 закрыт ратификацией (PD-59) · 4 риском · **62 открыто (17 minor, 45 info), из них 0 major** — PD-113 закрыт P6 вместе с приходом эмиттера. Форма — 15 секций (добавлены эра P6 и дофикс P6). - **Промт P5 ВЫДАН оркестратором 10.08** (`PLATFORM_P5_SESSION_PROMPT.md`, D39.128): П-9 `POST /books` (+PD-72 потолок тела; развилка «кто пишет book.yaml при интейке» — вопросом в этот журнал ДО стройки той половины) · стоп/резюм-ручки PD-140 (различение «потолок vs авария» — за эмиттером движка, строки 103/165, его промт выдан параллельно) · наблюдаемость П-11 + печать конфигурации PD-114 · DEFECT_REGISTER секциями (пинг №16). Запуск — по слову владельца. *(→ исполнен сессией P5 11–14.08, принят D39.130; промт с баннером-исходом — `archive/PLATFORM_P5_SESSION_PROMPT_2026-08-10.md`.)* Эскроу/uncertain (строка 136) — следующим денежным промтом, НЕ в P5. - Эры P0–P3 (вход OIDC · кредиты · админ-CLI · деплой · фикс-паки) — исполнены и залендены (D39.107/109/112/114); разделы — в [archive/platform-PROGRESS-P0-P3.md](archive/platform-PROGRESS-P0-P3.md). Стек и рецепт стенда — `STACK_DECISIONS.md`; критерии приёмки — `ENGINEERING_STANDARDS.md`. *(Записи паков P4–P6 с дофиксами (08–15.08) — в [archive/platform-PROGRESS-P4-P6.md](archive/platform-PROGRESS-P4-P6.md); вынесено 16.08 по слову владельца.)* ## Эра пака P7 (16–20.08) — в архиве Пять актов, приёмка, фикс-раунды и таблицы селф-ревью выселены срезом в [`archive/platform-PROGRESS-P7.md`](archive/platform-PROGRESS-P7.md) (норма: закрытая эра не живёт в журнале). Итог и то, что пережило пак, — шапка выше и пинг оркестратора №18 ниже. ## Закрытые эры P0–P3 — в архиве Разделы сессий P0–P3 и их ратификаций (04–08.08) вынесены в [archive/platform-PROGRESS-P0-P3.md](archive/platform-PROGRESS-P0-P3.md) (D39.124). Решения оттуда живут в D-логе и `DEFECT_REGISTER.md`. ## Пинги оркестратора (живые ссылки для следующих сессий) **Пинг оркестратора №15 (09.08, D39.122): движковый пак «блокеры контракта» ПРИНЯТ и заленден `0e69bc1` — пять поверхностей для платформы существуют. ФИНАЛЬНЫЕ формы (менялись трижды за приёмку — старые в переписке игнорировать):** - **Манифест глав:** `.manifest.json`, `manifest_version: "tm-manifest-v2"` (v1 движок сам отклоняет); id главы = 16 hex (стабилен через пере-нарезку); **`unit.id = ::`** — тег разреза 8 hex, при любой смене нарезки/данных пары unit.id УМИРАЮТ намеренно (id жив ⇒ якорь цел); поле `heading` — ВРЕМЕННЫЙ рендер движка «Глава N», НЕ метка книги (решение владельца 09.08; настоящие заголовки — строка 160 бэклога движка). $0-команда `tmctl manifest` строит дерево до первого прогона (первое касание создаёт БД проекта — то же поведение, что у status). - **Прогресс:** `status --json` → `progress: {draft:{done,total}, edit:{done,total}}` на книге и в каждом элементе `chapters`; `done` = «разрешено волной» (ok/flagged/skipped); волна, которой нет, — `total: 0`; процент не отгружается — собирает клиент. - **Банк:** `.bank.json` (весь банк тремя статусами; id термов — длино-префиксированный хеш ключа уникальности, стабилен через пересборку) и `.bank-stop.json` (полная таблица подписи; `conf: null` ≠ 0). ⚠ **ИСПРАВЛЕНО оркестратором №18 (20.08, линза шва P7): «ВСЕ сайдкары атомарно» — НЕВЕРНО.** Атомарны (temp+`Sync`+rename, `backend/internal/pipeline/artifact.go:24-59`) ровно три — `manifest.json`, `bank.json`, `bank-stop.json`, — и читать во время прогона можно ИХ. `bank-stop.txt`, `mined-signature.yaml`, `auto-bank.yaml` пишутся `os.WriteFile` с усечением первым делом: читателей у них сегодня нет, и сессия, взявшая любой по прежней формулировке, получила бы усечённый документ на живом прогоне. - **Потолок (строка 145): `tmctl translate|redrive --ceiling-usd ` — это КНИЖНЫЙ ПОТОЛОК В СИЛЕ, не бюджет прогона** (ратифицировано D39.122): леджер сравнивает значение с накопленным committed+reserved книги. Пересчёт пользовательского «прирост в главах» (D39.110) в абсолют — ОБЯЗАННОСТЬ платформы. **⚠ АМЕНДИРОВАНО D39.123 (PD-158): формула = `committed_usd + прирост×оценка`, БЕЗ reserved** — read-only `status` показывает leftover-reserved, который `store.Open` зануляет до первой судимой резервации; включение переплачивало бы запасом сверх холда (исполнено обеими формулами против гейта). `reserved_usd` читается обязательным полем как гард присутствия и улика leftover. Значение ниже уже потраченного откажет первой же резервации. День-потолок не перекрывается. Опция по желанию зоны: движок готов провести `--ceiling-usd` и в `status`, чтобы мониторинг видел действующий потолок capped-прогона (сегодня status показывает книжный) — скажите, заведём строку. ## Пинг оркестратора №16 — 09.08.2026 (форма DEFECT_REGISTER, строка 167) Реестр дефектов дорос до 171 строки одной таблицей; докс-аудит D39.125 (worksheet `docs/archive/reports/DOC_AUDIT_INVENTORY_2026-08-09.md`, жалоба «зонная изоляция прячет половины тем») просит форму: разложить таблицу на СЕКЦИИ (open по весу · accepted-risk · fixed по эрам паков), сохранив построчную форму `| PD-N | … |` — `docs/scripts/counts.py` ключуется формой строки, не позицией, секции его не ломают. Заведите П-строкой в свой BACKLOG, исполнение — попутно следующим паком, НЕ срочно. Вопросы — через владельца. ## Пинг оркестратора №17 — 15.08.2026 (migrate принят, D39.134 — движковая половина самолечения ФИНАЛЬНА) Бэкенд-пак `tmctl migrate` принят и заленден (`d55edd4`): exit 13 = `schema_mismatch` финализирован, токен `schema_mismatch found=N expected=M` на stderr стабилен, write-путь тоже отказывает БД новее бинаря. Ваш PD-201 (самолечение «поймал 13 → migrate → повтор») теперь можно строить — движок больше не сдвинется под ногами; строку реестра, ссылающуюся на «движкового migrate ещё нет», обновите. Хвосты вашей зоны, найденные приёмкой (вход промта P7, исполнение попутно): (1) `deploy/README.md:131` цитирует МЁРТВЫЙ текст ошибки схемы («schema vN … expects vM») — движок его больше не печатает; (2) та же мёртвая цитата и уплывший якорь `store.go:135-143` в строке П-1 вашего BACKLOG; (3) комментарий-образец в `tmplatformctl` (`runs.go:45`) зовёт голый `tmctl` из PATH — копипаст воспроизводит тихий no-op СТАРЫМ бинарём при живом деадлоке, ваш же README:139-141 требует версионированный путь. Вопросы — через владельца. ## Пинг оркестратора №17 (второй) — 15.08.2026 (S4 принят, контракт 0.2.3 в каноне — пять строк вашего регистра протухли; аудит корпуса) Фронт S4 принят и заленден (D39.135), контракт **0.2.3** в каноне. Для вашего регистра: (1) **PD-172 · PD-173 · PD-174 · PD-180 — диспозиция «спек-правка 0.2.3 задачей S4» (D39.130 п.2в) ИСПОЛНЕНА** — правила стоят в `openapi.yaml` §createBook (PD-172 при этом уточнён ПО ВАШЕМУ коду: чтение останавливается на файле, обязательное после файла = 400 «как не слали», необязательное молча теряется — D39.135 п.2б); обновите статусы. (2) **PD-199 ратифицирован закрытым ещё D39.132 п.2а** («null на проводе подтверждён»), в регистре до сих пор open — привести. (3) **Ваше НОВОЕ обязательство в P7** (D39.135 п.2в): читать `BookIntake.title` (сегодня падает в «unknown field is IGNORED», `v0.go:369-376`) и проецировать `books.reject_reason` на провод (сегодня «kept for an operator and never projected», `v0.go:497`) — без этого 0.2.3 остаётся обещанием без носителя. (4) Кандидаты в P7 по аудиту корпуса 15.08: **PD-219** (упавший дрейн теряет хвост `unit_resolutions` навсегда — зона сама вешала его на читающую поверхность) · **PD-217** (книга на вечном холде блокирует апгрейд движка — вторая половина после D39.132) · **PD-162** (удалённый каталог книги = вечный прогон с открытым холдом). (5) ⚠ Ваш же журнал честно фиксирует: «`tmctl migrate` живьём не гонялся» — команда теперь СУЩЕСТВУЕТ и принята (D39.134); мнение оркестратора: прогнать деплой-рантбук end-to-end на дев-стенде настоящим migrate — обязательное предусловие первого выката, дешевле любого пака. Вопросы — через владельца. ## Пинг оркестратора №17 (третий) — 16.08.2026 (контракт-ревью принято D39.138: ваша половина батча 0.3.0, PD-104 закрыт, попутные находки) Контракт-ревью API v0 отработало отдельной сессией и ПРИНЯТО (отчёт — `docs/research/28-contract-review.md`; решения владельца — его §8, ратификация D39.138). ⚠ Читать ОРИГИНАЛ отчёта, не этот пересказ — он и есть носитель. 1. **PD-104 ЗАКРЫТ словом владельца 16.08** (§8 п.14): `SignupGrantMicroUSD` → **0** на бете, начисление руками; возврат $5 — вместе с суточным агрегатным потолком, когда появятся платежи. Исполнение — P7-однострочник конфига + тест (грант при неверифицированном email уже 0, `login.go:306-308`). Обновите регистр. 2. **Ваша половина батча 0.3.0 — ПОСЛЕ лендинга спеки; состав — §5 отчёта:** `wireProgress` → один счётчик (Б-0; ваши колонки и словарь шва НЕ трогаются — фазы остаются внутренним делом); словарь машинных `code` + `request_id` в `Problem` (Б-1/§8а: класс 1 — конкретика максимальная, класс 2 модельный — ОДИН грубый код без вариации); `GET /capabilities` (Б-2: пары/порог интейка/форматы/страницы/версия — всё уже есть в конфиге и константах); `WWW-Authenticate` на 401 и две причины 403 (Б-15); трейлинг-части интейка → 400 вместо тихой потери + `Location` на 201 + `Idempotency-Key` (Б-3); подрезание `limit` до максимума вместо сброса к дефолту (Б-10, `books.go:509-511`); `PATCH /books/{id}` title-only + `DELETE` + `GET /runs/{id}` + `Run.failure_reason` (Б-5/Б-8 — словарь исхода у движка уже есть); `blocked: {code, book_id}` на run-options/409 (В-6); отказ по неподдерживаемой паре кодом на интейке (сегодня ja/en-книга умирает `failed`-ом на валидации конфига движка в КОНЦЕ пути — денег не сгорает, но причина не доезжает). 3. **Сеть (§5б; вход до первой живой книги под фронтом, строка 186):** gzip на текстовых ответах (НЕ на SSE) и `ETag`/`If-None-Match`→304 на списочных GET — ваше же требование `PLATFORM_DIRECTION.md:202-205`, батчем оно уезжает В КОНТРАКТ. Числа: дерево 2283 глав = 250 КБ на кадр `status`, фокус-рефетч 12 вкладок = 562 КБ. 4. **К-10 (пофазность у главы): НЕ СТРОИТЬ** — поправка приёмки: вердикт §6 отчёта («правка проекции») противоречит Б-0; фазы уходят с провода и у главы. 5. **Попутные находки §9** (сессия их адверсариально НЕ судила — проверьте у себя, заведите П/PD-строки по месту): (а) расчёт может висеть навсегда в двух легаси-путях (`reconcile.go:660-670` — попытка без базовой отметки; дев-путь без движка) — бюджета «сколько холд может висеть» не существует; (б) флаг «аккаунт остановлен» выводится сканом последних прогонов ВСЕХ книг (`pgstore/books.go:720-724`) — одна книга зажигает аккаунт; после PD-203 перечитать; (в) `ReadRunForSpawn` линеен по живым прогонам (`pgstore/runs.go:712`); (г) `/metrics` без аутентификации, защита — только 127.0.0.1 (`main.go:200-202`); (д) лимитер входа один на процесс (`login.go:128-129,165`) — один клиент упирает вход всем (выбор объяснён комментарием, следствие стоит записать); (е) сырой Go-текст ошибки лежит причиной карантина в БД (`reconcile.go:320`) — на провод не идёт. Вопросы — через владельца. **Аддендум к пингу №17-третьему (16.08, свип планировочных доков D39.139):** (1) `docs/PLATFORM_DIRECTION.md` §2 и `BACKLOG.md` П-7 держат «фри-тир дефолт $5» — ПРОТУХЛО против PD-104 (слово владельца 16.08: грант 0 на бете, начисление руками; D39.138 п.2л) — поправить тексты при P7. (2) Баннер §3 PLATFORM_DIRECTION («ратификация за оркестратором») закрыт ещё D39.132 п.2б: oapi-codegen — кандидат при P7, sqlc — привязан к P7, River-факт поправлен; баннер снять/заменить ссылкой на ноту. (3) §4 п.5 («сервер ВПРАВЕ склеивать события») сузится батчем 0.3.0: склейка только снимкам, `note` не склеивать (research/28 Б-6в). Вопросы — через владельца. **Дозакладка к §4.6 промта P7 (16.08, аудит готовности зоны; релей владельца — подтверди эхом):** к списку протухших текстов зоны добавь: `PLATFORM_DIRECTION.md` §4 п.8 («живого канала до-прогонных состояний нет») — решено книжным SSE 0.3.0, опрос запрещён нормой · §4 п.10 («персист манифеста — недостающий») — построен D39.122 · `:184` ссылка на несуществующий §3.3 стандартов · `platform/README.md:3-4` шапка «зона на P1» и «один тест-гейт» (гейта два) · `BACKLOG.md` П-17 — состав пака у́же промта (промт первичен). Расхождение ENGINEERING_STANDARDS:59 против DIRECTION:128 по oapi-codegen решаешь ты (промт §4.6). Всё — попутно, не отдельным заходом. ## Пинг оркестратора №18 — 20.08.2026 (P7 акт 5 ПРИНЯТ С ФИКС-ЛИСТОМ и ЗАЛЕНДЕН; контракт 0.4.0 ратифицирован; тело — D39.152/D39.153) **Вердикт: ПРИНЯТЬ С ФИКС-ЛИСТОМ.** Панель шести линз в изолированных копиях (слепая · контракт-конформность · деньги · шов · вне карты · ревью канона 0.4.0 другой моделью) + пере-раны и собственные посадки оркестратора. Регрессов против HEAD нет, ни одной линзы с REJECT. **Пере-проверено МОЕЙ рукой (заявление = команда):** `make check` с обоими гейтами — 18 пакетов, EXIT=0, **скипов 0**, линтер 0 issues · реестр 327 строк / 65 открытых (13 minor, 52 info), major и BLOCKER нет — `python3 docs/scripts/counts.py --check` · оба док-гейта зелёные · `tmplatformctl books --migratable` живой (exit 0, «resumable run» на стендовой книге) · миграционный манифест сходится, released-миграции не тронуты, комментарий про пере-отпечаток 00016 честный. **Замер, обосновавший разворот 00016→00022, пере-выведен независимо** (свой бенчмарк на трёх вариантах в копии дерева): джойн **14.6 мс** · счётчик **5.3 мс** · без счётчика **2.9 мс**. Порядок и вывод подтверждаются, разворот законен. ⚠ Процент назван в ТРЁХ носителях тремя разными числами (журнал «83%», `perf_test.go:13` «96%», миграция 00022 «16.8 против 3.4, джойн сам по себе 9 мс»); мой замер даёт ≈80%, а «96%» ни из чего не выводится — один носитель на факт, остальные указывают. **Собственные посадки мутаций ВНЕ вашего списка — 8, поймано 7:** долг не поставлен третьей концовкой ✅ · `edit_wave` не монотонен ✅ (⚠ сообщение пина печатает указатель вместо значения — `readmodel_test.go:918`) · снят `Vary: Accept-Encoding` ✅ · HEAD лишён валидатора ✅ · `reopen` снимает стоп банка кому угодно ✅ · погашение долга без сверки метки ✅ · вынос метки долга из закрывающей транзакции во второй оператор ✅ (техника `xmin` §36 работает, проверено исполнением). ⚠ **НЕ поймано — дыра:** снятие `structure_version` И `revision` из `emitFrame` (`pgstore/events.go:72-73`) проходит ВСЮ батарею. Это два поля, которые канон требует на КАЖДОМ кадре (`EventBase`), и ваш же комментарий это утверждает. По вашей норме PD-1 свойство без пинящего теста считается НЕ закрытым. ### Фикс-лист приёмки — завести строками СВОЕГО бэклога и регистра (зона моя не пишет) ⚠ **ПЕРВЫМ — не из пака P7, но найдено вторым рубежом приёмки и проверено мной построчно: `Sweep` останавливается для ВСЕЙ инсталляции на двух медленных прогонах, и выхода нет.** `cmd/tmplatformd/runner.go:189` — `sweepBudget = 2 * time.Minute` на ВЕСЬ проход; `internal/runs/reconcile.go:70` — `defaultRunBudget = 60 * time.Second` на ОДИН прогон. 120/60 = 2: два прогона, выбравшие свой бюджет, съедают проход целиком, и тогда `ctx.Err()` (`reconcile.go:39,48`) обрывает цикл — **`UnsettledRuns`, единственный ретрай отложенного расчёта, не вызывается вообще**, а живые прогоны, стоящие ниже, не реконсилируются и не спавнятся. `ListLiveRuns` сортирует `order by r.started_at` (`pgstore/runs.go:249`), то есть заклиненный прогон — по построению самый старый — стоит в голове и голодит остальных КАЖДЫЙ проход, детерминированно. Ручки нет: `runs.Config.RunBudget` объявлен (`internal/runs/runs.go:62-65`), но `startRunner` его НЕ присваивает (`cmd/tmplatformd/runner.go:74-83`), переменной окружения нет ни для одного из двух чисел, а `tmplatformctl` не умеет ни закрыть прогон, ни вернуть холд. Цена: прогон вечно `finished_at is null` ⇒ холд не возвращается ⇒ `runs_one_live_per_book` не даёт запустить новый прогон этой книги. Деньги заморожены, книга заморожена, пользователю видно «идёт». **Достижимо буднично, без экзотики:** `RunSink.Apply` берёт транзакцию с блокировкой книги на КАЖДУЮ строку журнала (`pgstore/sink.go:80-104`), поэтому после часа простоя демона две книги с бэклогом выбирают проход целиком — и всё время догона деньги всей инсталляции не считаются. ⚠ **`PD-169` в вашем регистре стоит `fixed(P5)` и этим ЛЖЁТ приёмке:** его пин гоняет `Sweep` вообще без дедлайна прохода, то есть доказывает пер-прогонный бюджет, а не выживание прохода. Пере-открыть. Код приехал в P6 и в дифф P7 не входил (`Sweep` против HEAD — identical), поэтому ни одна диффовая линза его увидеть не могла: тот же класс «композиция известных фактов», что и строка 198. ⚠ Наблюдаемость при этом ЕСТЬ и она ваша: `tm_platform_sweep_unfinished_total` растёт — то есть оператор увидит, что проход не дошёл до конца, и не сможет ничего сделать. Метрика без ручки — половина механизма. 1. **`ContractVersion` = "0.3.0" при формах 0.4.0** (`internal/httpapi/capabilities.go:13`, он же в каждом `hello` — `stream.go:100`). Ратификация 0.4.0 состоялась (D39.152) ⇒ константа обязана подняться. Комментарий над ней сам это требует: «raised in the same commit as the code that implements a new minor». Сегодня практического вреда нет (фронт заморожен на 0.2.3), но деплой объявляет версию, которую не отдаёт. **Первым пунктом.** 2. **Пины на два поля `EventBase`** (см. выше). ⚠ Попутно 21.08 усилен гейт якорей доков: `file:line` теперь может нести токен ожидания (`` `путь:12-14`=`подстрока` ``), и такой якорь сверяется ПО СОДЕРЖИМОМУ на каждом прогоне, включая коммиты, которые двигают ЦЕЛЬ. Ваши доки (`platform/docs/**`) вошли в область линта — раньше их не сканировал никто. 3. **Бюджет попыток материализации.** У долга нет предела и канала «признать безнадёжным»: claim → fail → defer(`now()`) → следующий свип через 15 с, вечно (`pgstore/books.go:538,557-564`). У интейка предел есть (`parseAttempts`=5). Три следствия: до 5 мин движковых процессов на проход бесконечно · поток такой книги НЕ заканчивается никогда (`AtRest` требует `read_model_owed_at is null`, `events.go:125`) · при живом манифесте и падающем экспорте `SaveStructure` коммитится каждый проход ⇒ `revision++` и кадр каждые 15 с. 4. **sqlc — БЕРЁМ, слово владельца 20.08 («я вообще за»).** Отступление P7 закрыто, PD-44 переоткрыть исполнением. ⚠ Ваше предложение «взять на однооператорных ручках P8» покупает инструмент туда, где не болит: рантайм-ошибки «нет такой колонки» случились в СКЛЕЕННОМ SQL read-модели (⚠ счёт: проверяемый носитель — ваш регистр PD-44 и журнал — несёт ДВЕ; ещё две вы назвали в ответе владельцу, в доки они не попали, поэтому опираемся на две — испр. 20.08) (`chapters_before`/`stop_for_signing` — `readmodel.go:441,446`, там же `note_count` и сломанный алиас `b`), куда sqlc по построению не дойдёт. Тем же паком — гейт, который туда дойдёт: прогонять КАЖДЫЙ собранный запрос через разбор Postgres (prepare/describe) против мигрированной схемы; склейка ему не мешает, он получает финальную строку. И ответить попутно на вопрос, которого мы не знаем: **есть ли в read-модели запрос, которого не касается ни один тест** — если есть, это не «медленная обратная связь», а дыра. ⚠ **Половина ответа УЖЕ получена вторым рубежом приёмки** (собранный SQL извлечён из пакета через `go/types` и прогнан `EXPLAIN (GENERIC_PLAN)` против мигрированной схемы): **141 запрос, все планируются чисто, остаточных «нет такой колонки» НЕТ**; тестом ЭТОГО пакета недостижимы десять, но по батарее ЦЕЛИКОМ реально не покрыты только **три** (`DeleteOldLoginEvents` · `UserByIdentity` · `Observe`; остальные семь покрыты на 58–87% тестами соседних пакетов — пере-мерено покрытием при аудите 20.08, первая редакция называла десять и это было неверно) — `RefundParseAttempt` · `StuckIntake` · `OpenReservations` · `ReleaseUnspawned` · `DeleteOldLoginEvents` · `UserByIdentity` · `Observe` · `SpendBound` · `AttemptReservationOpen` · `RunPausedReason`, и ни один из них не трогает схему 00016–00024. То есть дыры сегодня нет, а гейт нужен как ПОСТОЯННЫЙ — именно он и делает этот ответ воспроизводимым. 5. **PD-297** — round-trip на строку в `SaveStructure` под эксклюзивной блокировкой книги; мерить на корпусной книге до и после (корпусная книга появится на холодном прогоне движка). 6. **Труба доставки решений банка в движок** — единый бэклог, строка **199(а)**: перед resume писать решения в `mined_delta`/`mined_rejects`. Сегодня `action`/`dst` — write-only колонки (единственный SELECT `readmodel.go:336` проверяет лишь наличие строки), а воркер описан в комментарии вашей же миграции `00002_readmodel.sql:172-174` и не построен. 7. **Мусор:** пустой `platform/ru` (0 байт, обрубок редиректа) — в лендинг не взят, снесён оркестратором. `books.go:234-235` — задвоенная первая строка доккомментария, класс PD-310/326. 8. **Ревью-пак четырёх осей, которых не смотрел НИКТО** (ваш же obstacle): деньги и леджер целиком · вход/сессии/CSRF · очередь и джобы · метрики. Это не дофикс и не довесок — первый взгляд, отдельной работой. 9. **Один носитель на факт для замера 00016→00022 — но НЕ удалением числа.** Выигрыш назван ЧЕТЫРЬМЯ носителями: шапка этого журнала «83%» · `internal/pgstore/perf_test.go:13`=`96% of the page` «96%» · миграция 00022 «16.8 против 3.4, джойн сам 9 мс» · регистр PD-306 (повторяет 83% и 16.8/3.4). ⚠ Я сперва записал, что «96%» ни из чего не выводится — **это была моя ошибка, снята проверкой записей**: оно выводится точно из ВАШЕГО же замера в `archive/P7_ACT5_FIX_PLAN_2026-08-20.md:444-446`=`636 мс против` («636 мс против 24 мс», холодный корпус акта 4) = 96.2%, тогда как 83% и мой независимый ≈80% — с вакуумированного корпуса. То есть носители меряли РАЗНОЕ и все честны. Свести указанием УСЛОВИЙ замера при каждом числе, оставив нормативным один (миграция 00022 — она их и несёт); удалять «96%» как фантом НЕЛЬЗЯ. 10. **У материализатора нет ПОЛА на пустой манифест — латентная потеря всего текста книги.** `internal/readmodel/readmodel.go:215-232` строит `in.Chapters` только из `manifest.Chapters` и ни разу не сверяется со счётчиками того же документа (`ChaptersTotal`/`UnitsTotal` лежат рядом и печатаются в лог строкой ниже). Пустой список едет в `SaveStructure`, где `pgstore/readmodel.go:193-196` выполняет `delete from chapters where book_id = $1 and not (id = any($2))` — на пустом массиве предикат истинен для ВСЕХ глав, и каскад `chapters → units` сносит текст; при пустом `Key` вдобавок срабатывает `changed` и уходят все `unit_resolutions` (родня строки 198). Сегодняшним движком недостижимо (`buildManifest` всегда наполняет главы, файл пишется атомарно) ⇒ фикс-лист, не блокер. **Асимметрия и есть находка:** на ИНТЕЙКЕ ровно этот случай отловлен явно и прибит мутацией (`internal/books/parse.go:134-142`, `books_test.go:1296`) — «нет глав, но работа считается» там не признаётся правдой о книге. У материализатора такого пола нет, и теста на нулевой манифест в `readmodel_test.go` тоже нет. Лечится одной сверкой `len(manifest.Chapters)` против `manifest.ChaptersTotal`. 11. **`PD-327` в регистре стоит `open`, а канон 0.4.0 РАТИФИЦИРОВАН** (D39.152) — то есть условие её закрытия наступило. Закрыть строку регистра явно; сегодня расхождение видно скриптом (`counts.py --check` печатает PD-327 среди открытых), и это ровно тот класс, ради которого регистр объявлен источником истины по статусу. 12. **Изоляция читающих чтений НЕ ЗАПИНЕНА (аудит 21.08, посадка мутации).** Снятие `RepeatableRead`+`ReadOnly` у `inReadTx` (`internal/pgstore/credits.go:492-493`) проходит ВСЮ батарею — при том, что под этим инвариантом лежат шесть ручек выдачи, а носитель прямо объясняет цену («every frame in that window was lost for good», `books.go:684-688`, PD-163). По вашей же норме PD-1 свойство без пинящего теста считается НЕ закрытым — а это тот самый класс, которым я мерил найденную дыру с `emitFrame`. Код приехал НОВЫМ в P7, то есть лежал внутри диффа, который читали шесть линз приёмки и я сам. 13. **`CreditHeldBy`: оговорка `and book_id <> $2` не запинена (аудит 21.08, посадка мутации).** Мутант, снимающий её, проходит батарею: пин, который выглядит покрывающим, её ни разу не исполняет — в фикстуре у исключаемой книги холда НЕТ (`internal/pgstore/credits_test.go:443-465`). Сумма кормит контрактный `blocked`, и раздутая сумма отправляет пользователя гасить прогон, который ничего не освободит — ровно тот вред, против которого написан комментарий над самим запросом (`credits.go:393-397`). ### Что ушло в ЕДИНЫЙ бэклог (движок/шов/контракт — ваши строки туда не заходят, D39.84) **198** апгрейд движка стирает замечания и счётчики безвозвратно (ваша зачистка при ре-кате × announce-once движка — композиция, гейт холодного прогона) · **199** канал доставки правок банка · **200** сквозная полоса прогресса вместо пофазной (слово владельца 20.08) · **201** «Глава N» внутри текста экспорта · **202** живой прогон насквозь через API — ПОСЛЕ холодного прогона движка (слово владельца 20.08) · **203** хвосты контракта · **204** движок публикует причины флагов данными (релей §7в, ваш PD-246 — строка заведена в бэклоге ДВИЖКА, как вы и просили). ⚠ По релеям §7 сверено грепом при лендинге, а не по вашему списку: **(б) и (д) уже ИСПОЛНЕНЫ** синком 0.4.0, **(г) наполовину** (`unspecified` ратифицирован, открыта граница ступеней), **(и)** закрыт полем `stop_requested`. Реально открыты только (а)-канон-половина, (з) и договорная часть (к). ### Ратификации, которые вас касаются **Контракт 0.4.0 РАТИФИЦИРОВАН (D39.152)** — PD-327 закрывается лендингом канона, он состоялся. ⚠ Ваш клейм «ломающая правка ровно одна» верен для диффа генерённых ТИПОВ и неточен поведенчески: против 0.3.0 расходятся ПЯТЬ мест — пятое, семантика `410 Gone`, принесена проверкой записей и названа вашим же PD-253 (`stop_requested` · тождество интейка по содержимому против дословного «never over the bytes themselves» · `409` там, где таблица резюма говорит `202` · валидатор на двух ручках сверх объявленных). Все четыре 0.4.0 благословляет, поэтому цена уплачена ратификацией — но клейм в отчёте стоит поправить, чтобы следующая приёмка не опёрлась на него. **Оговорка про пер-термные решения расширена на ВСЮ ручку** (была только на `decline`): инертны одинаково и `approve`, и `dst`. Оговорка временная — снимается исполнением строки 199(а). ⚠ **Исправление МОЕГО же пинга №15 (испр. оркестратором №18):** «Все сайдкары пишутся атомарно (temp+rename)» — НЕВЕРНО. Атомарны три из шести (`manifest.json`, `bank.json`, `bank-stop.json` — `backend/internal/pipeline/artifact.go:24-59`); `bank-stop.txt`, `mined-signature.yaml`, `auto-bank.yaml` пишутся `os.WriteFile` с усечением. Сегодня безвредно (читателей нет), но сессия, взявшая любой из них по той строке, получит усечённый документ на живом прогоне. **Сессия P7 ЗАКРЫТА владельцем 20.08.** Промт отработан → `platform/docs/archive/`. Дальше — другие сессии.