textmachine/platform/docs/platform-PROGRESS.md

148 KiB
Raw Blame History

Журнал зоны «Платформа»

Весь прогресс платформы — ЗДЕСЬ (решение владельца 04.08): пинги, итоги сессий, открытые вопросы, предложения на ратификацию. В docs/PROGRESS.md платформа не пишет; оркестратор читает этот журнал при каждом лендинге зоны (свип «решений владельца» — норма D39.99 п.4).

Текущее состояние

  • P8-FIX ПРИНЯТ И ЗАЛЕНДЕН оркестратором №18 (22.08). Приёмка велась слепой линзой ДО чтения отчёта: сперва пере-прогон, счёт тестов и собственные посадки, и только потом сверка с запиской зоны. Что сверено ИСПОЛНЕНИЕМ, а не принято на слово:

    • Батарея на моём стенде: 18 пакетов, exit 0, линтер «0 issues» — сходится. ⚠ «Скипов 0» НЕ воспроизведён: у меня три. Это не регрессия пака и не ошибка отчёта — systemdOrSkip гейтит три теста internal/runner достижимостью пользовательского менеджера systemd, а на моём хосте /run/user/1000 не существует. Отчёт мерил на хосте с живым менеджером и там 0. Незакрытым остаётся то, что рецепт объявляет у батареи ДВА гейта, а их три — PD-374.
    • Тесты 561 против 532 на HEAD — сходится, сверено git show HEAD:<файл> по всем _test.go против дерева. Удалённых имён ровно три, все три описывали снятую пер-термную подпись.
    • Отпечатки миграций: пере-считаны все 25, расхождений нет (⚠ испр. 22.08 аудитом доков: первая редакция этой строки писала «41» — число взято из головы, а не из команды; grep -c '^[0-9a-f]\{64\}' migrations.sha256 = 25, файлов миграций тоже 25); в диффе migrations.sha256 ровно одна ДОБАВЛЕННАЯ строка — заявление «релизные миграции не тронуты» держится механически.
    • Пол поверхности 15 → 14 равен реальной длине таблицы маршрутов (14), то есть запаса не оставлено и следующее сужение гейт поймает. Причина стоит в комментарии рядом.
    • Гейты доков: --check exit 0 · --lint exit 0. Пинг №1 зоны закрыт: якорь 05-decisions-log.md:542perf_test.go:13 жив, токен «96% of the page» на месте. Правку ЧУЖОГО промта (пере-нацеливание двух моих якорей после переезда констант) утверждаю и не откатываю: якоря умерли по вине пака, пак их починил и раскрыл это сам — это ровно то поведение, которого норма и требует.
    • Деньги — двумя путями из СЫРОГО леджера, своей живой пробой (свой дев-демон на дереве пака, своя БД): до run abandon --release-hold кэш account_balances = 24910000 и sum(credit_ledger) = 24910000 при одном открытом холде 90000; после — 25000000 и 25000000, открытых холдов 0, строки grant · hold · hold_release. Холд вернулся целиком, книга разморозилась (следующий прогон 202).
    • Вся цепь PD-169 воспроизведена мной живьём, а не по отчёту: отсрочка считает неудачи между проходами (FAILS 6), переходит StalledAfter → гейдж tm_platform_runs_stalled 1 на проводе → tmplatformctl runs --stalled называет прогон, счёт, что держит и НАСТОЯЩУЮ ошибку движка. Обе новые ручки печатаются на буте с источником (SWEEP_BUDGET 2m0s default, RUN_BUDGET 1m0s default).
    • Одиннадцать собственных посадок мутаций вне списка автора. Убиты восемь: разделение фазы · «бюджет выбран = отказ» · очистка счёта тишиной · бэкофф · пропуск фазы расчёта на исчерпанном проходе · версия контракта · пол идентичностей манифеста · «стоп перевешивает отсрочку». ⚠ Две первых редакции моих посадок дали ЛОЖНЫЕ «выжила» — я гонял не тот пакет (пин лежал в internal/runs, а мутация была в internal/pgstore); пере-прогнано, обе убиты. Говорю об этом, потому что необъявленная ошибка харнесса приёмки — это находка, которой нет.
    • Выжили три, и они разные. StalledAfter — законно: носитель один, пин ездит на константе. maxAttemptsPD-373: второй носитель есть в рантбуке прозой. truncateReason в DeferRunPD-372: пин ездит на самой функции, ни один из четырёх вызовов не проверен.
    • Находка вне карты пака — PD-371: исключение «стоп перевешивает отсрочку» не имеет границы, и это видно живьём: NEXT TRY стоял на 30 минут вперёд, а счётчик рос каждые 15 секунд. Регистр после приёмки: 374 строки, открытых 72 (1 major, 16 minor, 55 info) — своих строк четыре (PD-371PD-374); зонные числа сдачи (370/68) остаются верны на момент сдачи и не переписываются. Открытым уезжает PD-370 (контрактная половина) — это НЕ работа зоны, и закрывать её здесь было бы подгонкой под критерий приёмки; согласен с диспозицией. ⚠ Порядок паков вышел ОБРАТНЫМ моей очереди, и это меняет рамку следующего: промт P8-FIX строкой 3 требует запуска «ПОСЛЕ пака P8-REVIEW», запущен он был раньше. Следствие в плюс: цена, которую я назвал вслух при перестановке — «блокер свипа живёт всё время читающего пака», — НЕ заплачена, блокер закрыт первым. Следствие в минус: §4.7 (аддендум релеем) в этом паке пустой и честно назван таковым, а читающий пак теперь пойдёт по коду, который этот пак только что переписал — его промт по оси 3 зона уже пере-нацелила на новые места, остальные три оси не затронуты.
  • P8-FIX — ФИКС-ЛИСТ ПРИЁМКИ P7 ЗАКРЫТ. Дерево готово к приёмке и лендингу (22.08). Батарея: 18 пакетов, exit 0, скипов 0, линтер 0 issues, скипов 0, тестов 561 (HEAD 532). Регистр — 370 строк, открытых 68 (14 minor, 53 info, 1 major) — сверено командой на момент сдачи (python3 docs/scripts/counts.py --check, exit 0; --lint exit 0; ⚠ вес регистра этим скриптом НЕ проверяется — числа сняты его печатью, а не гейтом). Открытый major — PD-370, контрактная половина спила пер-термной подписи банка: она не работа зоны, закрывать её здесь было бы подгонкой под критерий приёмки. Миграция пака: 00025. Дерево не закоммичено и не застейджено. Удалённых имён тестов ТРИ, и все три — по решению владельца 22.08 (сверено исполнением, не памятью: имена из git show HEAD:<файл> по всем _test.go против дерева): TestABadDecisionRefusesTheWholeBatchAndNamesTheItem · TestAnUnknownTermRefusesTheBatch · TestTheBankIsReplacedWhileTheDecisionsSurvive — они описывали пер-термную подпись банка, которую D39.144 отменил, а этот пак снял из кода (PD-370). Тест снятого кода — не потеря покрытия; третий переписан на СОХРАНИВШЕЕСЯ свойство (TestTheBankIsReplacedFromTheEnginesReadOut). Плюс один ПЕРЕИМЕНОВАН по факту изменения смысла (…GivesTheHoldBackOnlyWhenAsked…AlwaysGivesTheHoldBack, PD-361) — имя заведено этим же паком. ⚠ Пинг оркестратору №1 — ЕГО зона, сверить при лендинге: docs/architecture/05-decisions-log.md:542 держит якорь на internal/pgstore/perf_test.go:13 с токеном «96%». Пак ЭТОТ комментарий правил (PD-366: он лгал про свой же бенчмарк), токен я сохранил намеренно и --lint зелёный — но док чужой, поэтому говорю, а не считаю вопрос закрытым. ⚠ Пинг оркестратору №2 — попутная находка ВНЕ зоны пака: PD-369 / П-21ClaimIdempotency под конкуренцией отвечает 500 (12 падения на ~80 прогонов собственного теста). Файл в диффе пака не участвует, не трогал. ⚠ Пинг оркестратору №3 (PD-370, требует контрактной сессии): канон 0.4.0 несёт пер-термный BankDecision{action: approve|decline}, а D39.144 эту модель отменил 16.08 — подписывается весь банк одним ОК. Зона канон исполняет, снять в одностороннем порядке не может. Правка канона → правка зоны, в этом порядке. ⚠ Сдача идёт ПОСЛЕ второй волны ревью, которая нашла в самом паке блокер (PD-353) и два дефекта, делавших его собственные механизмы недостижимыми (PD-354, PD-355) — раздел «Волна 2».

    Записка-план по §4 промта: пункт → где исполнено / отказ с причиной

    Пункт Исход Где
    Блокер свипа (врезан первым) исполнен четырьмя механизмами ниже, отдельным разделом; PD-169 пере-открыт и закрыт заново
    1. ContractVersion 0.3.0 при формах 0.4.0 исполнен internal/httpapi/capabilities.go + гейт internal/gates/contract_test.go (PD-328)
    2. Пины на два поля EventBase исполнен, посадка сперва воспроизведена internal/pgstore/events_test.go (PD-329)
    3. Бюджет попыток материализации исполнен internal/readmodel/readmodel.go, миграция 00025 (PD-330)
    4. sqlc + гейт разбора SQL гейт построен; sqlc — граница названа, решение владельца internal/pgstore/sqlgate_test.go; разбор — PD-44, строка бэклога П-19
    5. PD-297 (round-trip под блокировкой) отказ с причиной нечем и не на чем мерить — ниже
    6. Труба доставки решений банка (строка 199а) не строил, сдаю вопросом — как промт и требует ниже, отдельным разделом
    7. Мусор: задвоенная строка доккомментария исполнен internal/books/books.go canTranslate (PD-335)
    8. Ревью-пак четырёх осей НЕ мой — заказ пака P8-REVIEW
    9. Один носитель на факт для замера 00016→00022 исполнен, с названным расхождением с буквой пинга internal/pgstore/perf_test.go (PD-336)
    10. Пол на пустой манифест у материализатора исполнен internal/ingest/manifest.go Whole(), internal/readmodel/readmodel.go (PD-332)
    11. PD-327 стоит open при ратифицированном каноне исполнен регистр, fixed(P8-FIX)
    12. Изоляция читающих чтений не запинена исполнен, посадка сперва воспроизведена internal/pgstore/isolation_test.go (PD-333)
    13. CreditHeldBy: оговорка не запинена исполнен, посадка сперва воспроизведена internal/pgstore/credits_test.go (PD-334)
    PD-255 (норма комментариев сменилась) закрыт решением владельца 21.08 регистр

    Блокер свипа: почему лечение — не «поднять константу»

    Диагноз пинга подтверждён построчно и воспроизведён исполнением. Живое свойство: проход 2 мин против 60 с на прогон ⇒ двух медленных прогонов хватает, чтобы съесть проход целиком, после чего ctx.Err() обрывает цикл и UnsettledRuns — единственный ретрай отложенного расчёта — не вызывается ВООБЩЕ, для всей инсталляции, каждый проход. Построено:

    1. Проход разделён на фазы. Реконсиляция получает половину остатка, расчёт — свой кусок (runs.phaseBudget). Выбранный бюджет фазы больше не ошибка свипа: очередь просто ждёт тика.
    2. Отсрочка с растущим бэкоффом на ПОПЫТКЕ (run_attempts.reconcile_after, миграция 00025). Свип читает RunsToReconcile, а не ListLiveRuns; порядок «старейший первым» не тронут — он верен для всего, что просто медленно, — а из ГОЛОВЫ заклиненный прогон убирает именно отсрочка. Телеметрия (Lag) по-прежнему видит всё.
    3. Терминальное состояние после N неудач (runs.StalledAfter = 5, ≈ четверть часа попыток): прогон считается ЗАСТРЯВШИМ, гейдж tm_platform_runs_stalled, одна ERROR-строка на пересечении порога — не на каждом тике.
    4. Ручка у ОПЕРАТОРА, которой не было вовсе: tmplatformctl runs [--stalled] называет строку, число неудач, следующую попытку, сумму холда и его возраст, юнит и последнюю ошибку; tmplatformctl run abandon --run <id> --reason <text> [--release-hold] — терминальный вердикт. Плюс books --abandoned и book refresh для второй половины (материализация).

    Границу «дальше не иду» называю явно: автоматически решать про деньги прогона, о котором нельзя спросить, этот пак НЕ стал. Правило реконсилятора везде — «я не смог спросить» ≠ «прогона нет»; прогон, упавший N раз, это ровно прогон, о котором никто не смог спросить, и счётчик перед той же ошибкой её не чинит. Автоматический ответ на этот вопрос и есть эскроу (строка 136, П-18), который промт §4.6 из пака исключает. Что счётчик покупает — что человека ИЗВЕЩАЮТ; что покупает ручка — что извещение чего-то стоит. Хвост записан строкой П-20, а не подразумевается.

    Оба числа стали ручками: TM_PLATFORM_SWEEP_BUDGET и TM_PLATFORM_RUN_BUDGET. Второе — отдельная находка пака: поле runs.Config.RunBudget было объявлено, задокументировано и никем не присваивалось (PD-331), то есть ручка существовала в структуре и не делала ничего.

    Пункт 4: что построено и где проходит граница sqlc

    Построен гейт, который доходит туда, куда sqlc не дойдёт: TestEverySQLStatementParsesAgainstTheMigratedSchema сворачивает КАЖДЫЙ SQL пакета прямо из исходника (литералы, конкатенации, именованные константы, плюс оба внутренних помощника, принимающих SQL параметром) и планирует его живым Postgres'ом (explain (generic_plan)) против мигрированной схемы. 162 оператора, все планируются. Несворачиваемый SQL — ОШИБКА гейта, а не пропуск: единственное исключение выписано таблицей в самом гейте (store.go Ready, где имя таблицы принадлежит goose). Тем же гейтом закрыт вопрос фикс-листа «есть ли в read-модели запрос, которого не касается ни один тест»: теперь его касаются все, на каждом прогоне батареи.

    Граница sqlc — мой выбор, аргумент: склеек в пакете 25 из 147, фрагментов-констант 15, у lastRun девять потребителей — read-модель для sqlc недостижима по построению, и это ровно та часть, где рантайм-ошибки и случались. Свободных от склейки файлов целиком пять: credits.go(15) · identity.go(13) · idempotency.go(7) · sessions.go(5) · observe.go(1) = 41 запрос. Это единственный кусок, где инструмент силён; конверсия — отдельный пак (одиннадцать денежных запросов идут внутри чужой транзакции, генерённый код коммитится, нужен пин версии и гейт «сгенерённое актуально»). Взять его здесь на две-три ручки — ровно то, от чего предостерёг фикс-лист: купить инструмент туда, где не болит. Заведено строкой П-19. ⚠ Вопрос закрыт владельцем 22.08: sqlc берётся ОТДЕЛЬНОЙ СЕССИЕЙ, не внутри пака — изменений много и мешать их с содержательной работой нельзя. Граница остаётся названной: 41 запрос.

    Пункт 5, PD-297: отказ с причиной

    Мерить нечем и не на чем. Сама строка регистра объявляет замер на здешнем стенде НЕпредставительным (unix-сокет против управляемого PG по TCP — разница на два порядка), а корпусной книги нет: она появляется на холодном прогоне движка, которым гейчена строка 202 (решение владельца 20.08). Правка самого опасного на запись пути без замера — ровно то, что эта строка запрещает. Берётся вместе с холодным прогоном.

    Пункт 9: расхождение с буквой пинга, названное вслух

    Пинг просил свести условия В миграцию 00022. Она РЕЛИЗНАЯ, и гейт неизменности (migrations.sha256 + TestReleasedMigrationsAreUnchanged) её правку запрещает; правка гейта ради этого была бы подгонкой под зелень, что промт и CLAUDE.md запрещают прямо. Поэтому 00022 остаётся нормативным носителем вакуумированных цифр как есть, а условия дописаны в носители, которые править законно. «96%» не удалено и получило своё условие: это ХОЛОДНЫЙ корпус акта 4 (636 мс против 24), тогда как 8083% — вакуумированный. Носители мерили разное и все честны.

    Строка 199(а): развилку НЕ решал — сдаю вопросом, как требует §4.2 промта

    ПОПРАВКА ВЛАДЕЛЬЦА 22.08, и она сносит посылку, на которой я строил разбор. Я описывал трубу как доставку ПЕР-ТЕРМНЫХ решений («approve/decline на термин»). Такой модели нет и не было с 16.08: D39.144 ратифицировал ОДИН ОК над ВСЕМ банком — «пер-термная подпись = сотни кликов — НЕ модель продукта», «в модели одного ОК сценария „какие 17 из 300 решены“ не существует». Пер-термно существует не подпись, а правка: поправить/добавить термин до подписи, и — после прочтения — поправить термин с пере-генерацией задетых глав (вторая половина гейчена строкой 192).

    Труба от этого не отменяется, но её ГРУЗ другой: не «решения», а правки терминов (src → новый dst, плюс добавленные) в mined_delta. Развилка ниже (кто объявляет ключи и кто создаёт файлы) от этого не меняется ни в одном пункте — она про владение book.yaml, а не про груз.

    Модель, которую D39.144 отменил, была у зоны ПОСТРОЕНА — 22.08 СНЯТА по слову владельца (PD-370). Что ушло: маршрут POST /books/{bookId}/bank/decisions, хендлер и wire-типы, метод интерфейса Library, SubmitBankDecisions, типы BankDecision/BankReceipt, UnknownTermError и их пины. На месте write-пути — пометка для будущей сессии (internal/httpapi/reading.go, блок «THE WRITE HALF OF THE BANK IS GONE»): что отменено, что ратификация ОСТАВИЛА пер-термным (правку), и что под неё уже есть в схеме. ⚠ Контрактная половина остаётся открытой — канон 0.4.0 несёт путь, три схемы и глагол, и это зона контрактной сессии.

    Посылку промта пере-проверил САМ, чтением кода движка, а не по докам. Подтверждается вся:

    Факт Улика
    движок читает mined_delta, только если путь объявлен backend/internal/pipeline/mining.go loadMinedDelta: if r.Book.MinedDelta == "" { return nil, nil } — ни одного чтения
    то же для mined_rejects там же, loadMinedRejects
    объявленный ключ с НЕсуществующим файлом валит загрузку конфига целиком backend/internal/config/book.go:199-207: bad("mined_delta %s is not readable: %v")
    ключей нет в шаблоне оператора backend/example/book.yaml — ноль вхождений mined_delta/mined_rejects
    платформа пишет book.yaml ОДИН раз и фиксированным набором из пяти ключей platform/internal/books/render.goO_EXCL, «writes the file once and never again»; ключи book_id · title · source_lang · target_lang · source_file
    новое, чего в промте не было: декодер движка СТРОГИЙ — незнакомый ключ это жёсткая ошибка backend/internal/config/book.go:142-144, dec.KnownFields(true) — то есть платформа не может «на всякий случай» дописать ключ, которого сборка движка не знает
    относительные пути резолвятся от каталога КНИГИ backend/internal/config/book.go:148-162, resolve() — значит один шаблон на все книги даёт пер-книжные пути без подстановки
    форматы файлов mined_delta = seed.File (terms: со схемой internal/seed/seed.go:63-80), грузится membank.LoadGlossarySeed и пере-штампуется Source:"mined"; mined_rejects = rejects: [{src, note}] (mining.go minedRejectFile), это ФИЛЬТР ПРЕДЛОЖЕНИЙ, в банк не входит
    на стороне платформы решения write-only internal/pgstore/readmodel.go SubmitBankDecisions пишет action/dst; ни один SELECT их не читает, воркер описан комментарием собственной миграции 00002_readmodel.sql:172-174 и не построен

    Варианты с ценой (решает владелец, не я):

    • A. Платформа объявляет два ключа при рождении книги и сразу создаёт два пустых файла. Работает без единой правки движка и без действий оператора; пустой файл законен (terms: пуст ⇒ ноль записей, os.Stat проходит). Цена: платформа начинает знать два ключа СХЕМЫ движка, а форма Б ратифицирована ровно наоборот (D39.110 §2б: шаблон — деплой-артефакт оператора, платформа подставляет только личность, языки и имя файла). Плюс строгий декодер: сборка движка, которая этих ключей ещё/уже не знает, перестанет грузить КАЖДУЮ новую книгу.
    • B. Ключи несёт шаблон оператора. Платформа схемы не узнаёт, пути пер-книжные достаются даром. Цена: сам по себе вариант НЕ работает — движок отказывается грузить конфиг с объявленным, но отсутствующим файлом, а создавать файлы всё равно некому, кроме платформы. То есть B требует половины A или варианта D.
    • C. Двигается ратифицированная граница владения: платформе разрешено писать в каталог книги эти два файла (и только их), ключи несёт шаблон. Цена: правка ратификации D39.110; зато платформа по-прежнему не знает схемы — она пишет файлы по путям, которые прочитала из уже существующего book.yaml.
    • D. Движок терпит объявленный, но отсутствующий mined_delta/mined_rejects как пустой. Одна правка backend/internal/config/book.go, после чего работает чистый B: платформа пишет файлы только когда решения есть, и ничего не обязано существовать заранее. Цена: ослабляется намеренный гард — опечатка оператора в пути становится тихой. Смягчается тем, что послабление только для двух ключей и с громким WARN. Это строка ЕДИНОГО бэклога (движок), не моя.

    Рекомендация зоны: C или D+B. Обе оставляют платформу в неведении о схеме движка — а это и есть то свойство, ради которого форма Б ратифицировалась. A решает быстрее всех и покупает это тем, что платформа впервые начинает знать схему чужой стороны, причём под строгим декодером. ⚠ Строить начну только после ратификации. Границы, если разрешат, приняты как в §4.2: писать не «перед resume», а только на том резюме, которое СНИМАЕТ стоп подписи (l.VerifyBank && !l.BankReleased); --verify-bank не трогать; пост-ридинговый цикл (строка 192) не проектировать. PD-277 в регистре относил эту работу к отложенной 192. ⚠ 22.08 он ПЕРЕ-ДИСПОЗИРОВАН — не по развилке, а по спилу глагола: остаток «честность decline» умер вместе с ним. Ниже — прежняя формулировка обязательства, оставленная как след решения: правлю его только после ратификации, как промт и велит.

    Как это проверялось

    Батарея: cd platform && make check с ОБОИМИ гейтами (TM_PLATFORM_TEST_DSN на живом PostgreSQL 18.4, TM_PLATFORM_TEST_ENGINE_BIN + _BOOK_TEMPLATE) — 18 пакетов, EXIT=0, скипов 0, линтер «0 issues». Дифф имён тестов — исполнением: grep -rho '^func Test[A-Za-z0-9_]*' по дереву против git show HEAD:<файл> по всем _test.go. ⚠ Замер пере-снят при сдаче и его исход изменился: comm -23 даёт ТРИ удалённых имени — все три описывали пер-термную подпись банка, снятую 22.08 по слову владельца (PD-370, разбор в шапке), — а comm -13 даёт 29 новых. Прежняя формулировка «удалённых нет» была верна до спила и снята, а не оставлена рядом с новой.

    Посадки мутаций волны 1 — 20, поймано 20 (перечень ниже; счёт пере-снят при сдаче — прежняя цифра «16» не сходилась с собственным списком в той же фразе). Каждая ставилась в код, пин обязан был упасть, код возвращался: снятие payload["revision"] · снятие payload["structure_version"] · ContractVersion назад на 0.3.0 · inReadTx без обеих опций · без изоляции · без read-only · ListBooks на inTx · снятие and book_id <> $2 · снятие пола самоописания манифеста · снятие пола «ноль глав» · обе фазы свипа на ОДНОМ дедлайне · ранний возврат фазы из Sweep · свип читает ListLiveRuns вместо списка «пора» · отсрочка только по ошибке · DeferReadModelDebt в now() · долг никогда не списывается · новая граница наследует потраченный бюджет · колонка, которой нет в схеме, в СКЛЕЕННОМ фрагменте (гейт SQL) · то же в локальной const q · несворачиваемый SQL . ⚠ Три посадки из этого списка сперва ПЕРЕЖИЛИ мои же пины и вскрыли настоящие дефекты пака — разбор в PD-337/PD-338/PD-339; пины переписаны, мутации пере-посажены.

    Воспроизведение ПЕРЕД пином — там, где промт этого требовал (пп. 2, 12, 13): три мутации посажены разом в HEAD-код и прогнана ПОЛНАЯ батарея — 18 пакетов, exit 0, скипов 0. То есть все три дыры подтверждены исполнением, а не прочитаны в пинге.

    Живые пробы — настоящий tmctl, живой Postgres, дев-демон на 127.0.0.1:8098:

    • GET /v0/capabilities"contract_version": "0.4.0" (пункт 1 на проводе);
    • tmplatformctl seed — аккаунт, грант, книга через ЖИВОЙ POST /v0/books: интейк прошёл, движок нарезал 3 главы, GET /chapters отдаёт 3, долг материализации погашен, read_model_attempts = 0. Это отрицательная проверка нового пола: законный манифест живого движка он НЕ отвергает;
    • PD-162 воспроизведён живьём — каталог книги унесён, прогон принят (202), спавн падает. tmplatformctl runs печатает строку с числом неудач 1, временем следующей попытки, суммой холда 0.090000, его возрастом, (none) вместо юнита и текстом ошибки движка. До этого пака оператор видел только растущий счётчик sweep_unfinished_total и не мог сделать ничего;
    • run abandon без флага: прогон failed/service_error, деньги не тронуты, книга РАЗМОРОЖЕНА — следующий прогон принят (202). С флагом --release-hold: холд вернулся целиком;
    • деньги пере-считаны из сырого леджера двумя путями и сошлись до цента: кэш account_balances.balance_micro_usd = 5000000, sum(amount_micro_usd) по credit_ledger = 5000000, открытых холдов 0; строки леджера — grant · hold · hold_release · hold · hold_release;
    • метрики на проводе: tm_platform_runs_stalled 0, tm_platform_reading_surfaces_abandoned 0;
    • обе новые ручки конфигурации печатаются на буте с источником (TM_PLATFORM_SWEEP_BUDGET 2m0s default, TM_PLATFORM_RUN_BUDGET 1m0s default) — норма PD-114.

    Гейты доков: python3 docs/scripts/counts.py --check — exit 0, литералы сходятся; --lint — exit 0. ⚠ Раскрываю правку ЧУЖОГО документа: пак убил два якоря в PLATFORM_P8_REVIEW_SESSION_PROMPT.md (константы sweepBudget/defaultRunBudget переехали), и --lint был из-за этого КРАСНЫМ. Якоря пере-нацелены на новые места, абзац оставлен как описание НАЙДЕННОГО, а не текущего кода. Правил чужой промт сознательно и говорю об этом прямо: якоря умерли по моей вине, а красный гейт не обходят. Оркестратор вправе откатить эту правку — содержательного текста она не трогает. ⚠ Первая моя проверка этого гейта дала EXIT=0 и была ЛОЖНОЙ: я мерил в момент, когда файла с якорями в дереве не было, поэтому линт его не сканировал. Критик полноты был прав, я нет.

    Спил пер-термной подписи банка (слово владельца 22.08) и что осталось контракту

    Заказ: модель отменена D39.144 ещё 16.08, реализации нет — спилить в зоне, оставить пометку будущим сессиям. Перед спилом — аудит воркфлоу: 5 линз (канон · код зоны · потребители · ратификация · будущая пометка) + адверсариальная проверка каждого рискованного вывода, 31 агент.

    Снято из зоны: маршрут POST /books/{bookId}/bank/decisions · хендлер bankDecisions и wire-типы (wireBankDecision/wireBankDecisions/wireBankReceipt) · метод SubmitBankDecisions из интерфейса Library и из стора · типы BankDecision/BankReceipt · ErrUnknownTerm / UnknownTermError и их отображение в контрактный код · пины, которые их описывали.

    НЕ тронуто, и это проверено: чтение банка (GET /bank, BankTerm, оси статуса и происхождения) · вокабула ДВИЖКА proposed|in_progress|approved в сайдкар-инжесте — она чужая и не отменялась · сам стоп awaiting_bank и подпись как resume (D39.144 снимала ГЕЙТ, не СТОП) · таблица bank_decisions и её констрейнты: миграции релизные, а колонки term_id/dst — ровно то, что нужно будущей ПРАВКЕ термина. Данных под снос не было: на стенде ноль строк решений.

    Пометка на месте write-пути (internal/httpapi/reading.go, «THE WRITE HALF OF THE BANK IS GONE»): что именно отменено, что ратификация ОСТАВИЛА пер-термным (правку и добавление термина), чего нет ни на одной поверхности, и что под это уже есть в схеме и в движке (mined_delta).

    Гейт размера поверхности сработалlen(contractSurface) < 15 поймал снятие маршрута. Пол сдвинут 15 → 14 с причиной в комментарии: пол, опущенный под ратифицированное сокращение, — не подгонка; опущенный без неё — подгонка. Два теста, стоявшие на снятой ручке как на «любом POST», пере-нацелены на живой маршрут, а не удалены.

    Насколько страдает контракт (главный вопрос владельца)

    Канон не лжёт про модельD39.144 в него внесена и записана в четырёх местах верно (openapi.yaml:443-448, :539-541, :707, :2498-2499). Беда в другом: рядом с новой моделью в полном рабочем обмундировании стоит старая — путь, три схемы, четыре счётчика, — и обе зовутся словом «signing». Файл несёт ДВЕ модели подписи.

    • Прямая ложь — шесть мест (четыре в каноне, два в компаньоне). Худшие: :1830-1832 («статус подтянется на следующей пересборке» — не подтянется, решения до движка не доезжают, что говорит сам же канон на :1959-1963) и компаньон :672POST /bank/decisions НЕ ПОСТРОЕНО» — было построено в P7 и снято сегодня).
    • Самопротиворечия — три. Самое явное: :443 «Signing the bank is ONE act over the whole of it» против :477 «Signing is not a row edit» — заголовок описания ручки называет подписью САМУ РУЧКУ. Тридцать четыре строки между ними, и оба текста компилируются в сгенерированный клиент. Плюс :1918-1922 («строка может быть решена и НЕ подписана, потому что отказ — тоже решение») — дословно отменённая модель — соседствует с :1929-1932 («INFORMATIONAL, never a gate»).
    • Дыра, которую спил вскрывает и которую надо назвать вслух: approve + непустой dst был ЕДИНСТВЕННЫМ живым носителем того, что D39.144 оставила пер-термным — «поправить термин». А «добавить свой термин» модель называет, и в контракте его нет вообще: на 17 путей канона запись в банк была ровно одна. То есть после спила у собственной фразы канона :539-541 («correct what they care to, then release it with ONE resume») нет ручки.
    • Счётчики вырождаются. pending_decisions считается как «строки proposed, на которые НЕТ записи в bank_decisions»; писателя больше нет ⇒ это «сколько строк предложено», а complete истинно ровно тогда, когда read-out движка не несёт ни одной proposed (не константа: сайдкар несёт и in_progress, и approved). Поля обязательные на проводе (EventBank.required), поэтому оставлены как есть: менять их смысл — правка канона, а не зоны.
    • Объём и вес: ~17 мест в openapi.yaml (~150 строк из 2592) + ~8 в компаньоне. Весломающий минор 0.4.0 → 0.5.0; мажора нет ни по политике («0.x ломает миноры весь бета-период»), ни по факту. Цена фронту нулевая ДОПОЛНИТЕЛЬНО: его зеркало ратифицированно стоит на 0.2.3.

    Пока канон не поправлен, деплой обслуживает на одну ручку меньше, чем объявляет. Это записано строкой PD-370 (зонная половина закрыта, контрактная открыта), и это осознанная цена спила, а не недосмотр.

    Волна 2: воркфлоу-ревью и что оно нашло в САМОМ паке

    Заказ владельца: после возврата ревьюеров волны 1 и починки их находок — воркфлоу пошире, 1030 агентов, один Fable 5 на тяжелейший участок. Исполнено: 11 линз поиска + адверсариальный рефутер на КАЖДУЮ находку + синтез, 47 агентов, 0 ошибок. Fable 5 — один, на свипе и деньгах.

    36 находок, 21 пережила рефутера, 15 опровергнуто. После склейки дублей (голодовку расчётной фазы назвали пять линз, срез по байту — две) это 17 строк регистра: PD-353…PD-366 закрыты, PD-367/PD-368/PD-369 открыты — они не мои решения и переданы как вопросы.

    Итоговая строка самого воркфлоу («ни одна находка не пережила проверку») — БАГ МОЕГО скрипта синтеза, а не вердикт. Она противоречила сама себе (пустыми были и «выжившие», и «убитые»), и таблица пере-собрана из journal.jsonl — по одной записи на агента. Говорю прямо: доверься я сводке, пак уехал бы на приёмку с блокером внутри.

    Блокер, найденный ПЯТЬЮ независимыми линзами (включая Fable 5) и подтверждённый мной по коду до их вердикта — PD-353. Пак разделил проход на фазы, чтобы одна не могла съесть другую, и оставил ВНУТРИ второй фазы то же самое голодание: UnsettledRuns упорядочен старейшим-первым, каждый элемент стоит целый бюджет, а settle при нечитаемой трате возвращает nil — то есть клин рапортует УСПЕХ, потратив проход. Одна недоступная книга замораживала холды ВСЕХ аккаунтов, каждый проход. Это ровно та формулировка, которую шапка Sweep объявляла побеждённой.

    Ещё два, которые делали механизмы пака недостижимыми:

    • PD-354 — счётчик неудач обнулялся ТИШИНОЙ: дорогой вопрос движку задаётся раз в ResyncEvery, остальные проходы ничего не устанавливают, и ClearRunDeferral на них сбрасывал счёт. 1,0,1,0 — StalledAfter недостижим, гейдж пуст, оператору не говорят никогда.
    • PD-355 — колонка SPENT в операторской таблице показывала пожизненную трату КНИГИ. Моя же починка G8 волны 1 взяла не то число, на единственной таблице, заведённой ради решения о деньгах.

    Чему меня научили РЕФУТЕРЫ (15 опровержений, каждое куплено исполнением, а не рассуждением):

    • «Поднять RunBudget = объявить здоровый прогон застрявшим» — опровергнуто замером: поднятие ручки не меняет вообще ничего, а строгая проверка RunBudget < SweepBudget/2 отвергла бы сами шиппящиеся дефолты. Я уже держал эту «починку» в плане и не построил её. Остаток — PD-368.
    • «Интейк обязан применить Whole()» — опровергнуто контрактом и батареей: вся батарея интейка ездит на манифестах без списка глав, контракт интейка — только счётчики. Механика находки верна (PD-367 открыт), лекарство — нет.
    • «Пропуск тела помощника в гейте SQL = дыра» — опровергнуто исполнением: мутация в теле bump краснит десятки поведенческих тестов; спрятать там непроверяемый оператор нельзя.
    • «Стойл-детектор слеп к форме PD-162» (находка Fable 5) — опровергнуто.

    Две линзы разошлись во ВЕСЕ одной находки (PD-358, срез по байту в операторской таблице): одна назвала микрополировкой вне рамки, вторая довела цепочку до многобайтового stderr движка. Починил как дешёвое и бесспорное, расхождение раскрыл в строке.

    Самопроверка (артефакт §5: имена агентов и по одной команде или цитате из каждого)

    Исходов три, «не проверено» ≠ «опровергнуто». Каждому агенту выдан СВОЙ каталог песочницы явным путём, мандат читающий, state-changing git запрещён перечислением (D39.113). В рабочее дерево не писал никто: оба ревьюера волны 1 ответили на прямой вопрос уликами.

    Волна 1 — три изолированных ревьюера по финальному диффу (author≠reviewer):

    Агент Ось Цитата или команда ИЗ ЕГО отчёта Находок Исход
    agent-afec473415b228cdf контракт-конформность против канона 0.4.0 + свои посадки «в песочнице создан симлинк <scratch>/docs -> …/docs — новый гейт internal/gates/contract_test.go тянется за канон ЗА пределы модуля, и без него копия дерева его не находит» 5 + 3 пережившие мутации все 8 починены
    agent-a394edded388676bc шов с движком и деньги, свой инвентарь каналов «repo*/platform/cmd/rev2probe|rev2ctl|rev2seam|rev2ceiling|rev2ids/main.go и repo*/platform/internal/runs/rev2_starve_test.go — мои пробы (только в копиях)» 8 + 3 пережившие мутации все 11 починены; G1 — БЛОКЕР в моём коде
    agent-a1e4407d89e52128d полнота сдачи: пункт заказа → носитель в дереве «Snapshot taken at 2026-08-22 00:49:24. ⚠ The tree was moving while I read it: platform/BACKLOG.md gained rows П-19/П-20 at 00:48, mid-review» 6 5 починены, 1 (К-4) вскрыла МОЮ ошибку измерения

    Две разведки (read-only, не ревью): agent-acf370d564663617e — инвентарь SQL для границы sqlc; agent-a1cb3d1a5c59f2f2e — инвентарь каналов движка.

    Волна 2 — воркфлоу wf_f8d1f3e6-85a, 11 линз (одна на Fable 5) + рефутер на каждую находку:

    Линза (агент) Предмет Цитата или команда ИЗ ЕГО отчёта Находок Пережило
    a2fdcd13144058b57 Fable 5 свип и деньги — тяжелейший участок «Стойл-детектор слеп к клину „юнит исчез, workdir недоступен“ (форма PD-162): реконсиляция каждый проход рапортует успех» 3 2
    ae8f856fe08ee9e61 фазы и отсрочка свипа «Счётчик отсрочек не может дорасти до StalledAfter для того самого клина, ради которого написан» 4 3
    a3c4dc82a7d62f8a6 архитектурный стиль, велосипеды «Фаза расчёта денег осталась без отсрочки: head-of-line starvation, от которой пак и построен, на списке UnsettledRuns не починена» 3 1
    a62786a00eb5bbae4 каноны Go и каноны зоны «RunBudget молча обрезан половиной SweepBudget» (опровергнуто) + голодовка расчётной фазы 4 2
    af8620a80b7e12b06 материализация и пол манифеста «Провалившийся экспорт на смене кадра затирает весь оплаченный текст книги» 3 3
    ada2420ac7c576856 CLI, метрики, конфиг «SPENT в операторской таблице — пожизненный счётчик КНИГИ, а не траты этой попытки» 3 3
    ac92e0a8acba148ec сам гейт SQL: даёт ли ложную зелень «unresolvable ключёвана функцией, а не сайтом: второй несворачиваемый запрос в той же функции молча проверяется текстом первого» 2 1
    a21f43459f3f12f7e комментарии: вода против несущего «perf_test.go копирует „нормативные“ цифры из миграции 00022 и в той же фразе утверждает, что не копирует» 4 1
    a8faf63f1dde50903 скоуп: сверка построенного против БУКВЫ промта «Пункт 9: новое условие замера в perf_test.go ложно для бенчмарка этого же файла» 4 2
    a4dad647c0eb8310f регрессии: что пак сломал в уже работавшем «Отмена контекста сообщается как дедлайн: остановка демона посреди прохода поднимает счётчик голодания» 3 2
    a178aca863af19915 контракт-конформность построенного против канона 0.4.0 «Пункт 1: подъём константы оставил в коде утверждение, что деплой объявляет 0.3.0» (опровергнуто) 3 1

    Рефутеры (по одному на находку, 36 штук) считаются наравне с находками — три примера того, чем куплены их вердикты:

    • ad12d9bcca05b883a: «WITH the index n_tup_upd=3942 n_tup_hot_upd=3909 (HOT 99.2%) idx size=8192 против WITHOUT the index n_tup_upd=1994 n_tup_hot_upd=1984 (HOT 99.5%)» — из этого замера выросла PD-364 (индекс удалён);
    • a337318688be20d30: «finished_at = now()books.go:313: column "finished_at" of relation "books" does not exist (42703) … на всех 5 сайтах»;
    • a7fadd3c541e9a87d: «пять проходов подряд дают „the sweep settled 2 of 3 finished runs: context deadline exceeded“, третий, чистый прогон не достигается никогда, reserved держится на $0.90» — это подтверждение блокера PD-353 исполнением.

    Посадки мутаций волны 2 — 6, поймано 6: фаза расчёта не отсрочивает · UnsettledRuns игнорирует отсрочку · тишина снова считается здоровьем · RunBudget снова не присваивается · срез по байту в операторской таблице · управляющие символы не вычищаются . ⚠ Две первые попытки посадить последние две дали НЕ падение теста, а ошибку сборки (осиротевший импорт) — это слабее, и они пере-посажены компилирующимися.

    Релей §4.7: не приходил

    Аддендум пака P8-REVIEW должен был доехать только релеем оркестратора с эхо-подтверждением. За сессию релея не поступало: ни находок по четырём осям, ни эхо. Отчёт читающего пака я не читал и не гадал по нему — промт это прямо запрещает, состав пака определяет оркестратор.

    Единственное касание того пака — техническое и раскрыто выше: два его якоря умерли от МОИХ правок кода, --lint покраснел, якоря пере-нацелены (docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md:62, плюс третье пере-нацеливание 22.08 после спила). Содержательного текста чужого промта не трогал.

    Obstacle: что не удалось и что не проверено

    • Сквозного живого перевода через платформу не было и в этом паке быть не могло: строка 202 гейчена холодным прогоном движка (решение владельца 20.08). Всё после «прогон стартовал и остановился» доказано батареей против живого Postgres и настоящего tmctl, а не переводом.
    • Труба 199(а) не построена и форматы сверены ЧТЕНИЕМ, а не записью файла: mined_delta и mined_rejects прочитаны в коде движка (membank.LoadGlossarySeed, mining.go), но ни одного файла платформа не писала — писать было бы строительством нератифицированной развилки.
    • Замер PD-297 не снят. Отказ осознанный: путь самый опасный на запись, править его надо с замером на корпусной книге до и после, а корпусной книги в паке не было.
    • Финальный дифф ПОСМОТРЕН независимым ревьюером (author≠reviewer) уже после спила — он и закрыл эту строку obstacle. Три его major были настоящими дырами в МОЁМ коде, и все три — отсутствие пина, а не ошибка логики: пол ErrTextUnknownForANewCut (охраняет единственную запись, способную стереть оплаченный текст) · «стоп перевешивает отсрочку» в RunsToReconcile · «пересборка банка не трогает bank_decisions» (свойство, на которое опирается пометка, ради которой спил и делался). Каждая закрыта пином, каждый пин проверен посадкой мутации. Плюс семь минорных: комментарии SaveBank и bankCountsTx описывали отменённую модель как живую, пометка врала про action NOT NULL, снятие NUL не было запинено. Не найдено дефектов там, где он искал отдельно: пол поверхности 15→14 равен реальному размеру таблицы, пере-нацеленные тесты проверяют то же свойство, лишнего не срезано.
    • Число скипов батареи снято рецептом make check, который пишет .check.log в дерево; при параллельных прогонах агентов лог обрезался. Отдельная сверка двух пакетов с условными скипами (internal/runner, internal/gates) дала --- SKIP = 0.
    • Контрактная половина PD-370 открыта: пока канон не поправлен, деплой обслуживает на одну ручку меньше, чем объявляет. Это цена спила, названная заранее, а не обнаруженная при приёмке.

    Инвентарь каналов движка (артефакт §4.4 — собран ЧТЕНИЕМ кода движка, не по нашим докам)

    Пак коснулся канала движка (пункт 10: ingest.Manifest.Whole() построен как зеркало движкового правила), поэтому инвентарь сдаётся таблицей, а не словами «я прочитал код движка».

    Канал Писатель в движке Атомарность Читатель/писатель платформы Согласовано?
    <project_db>.manifest.json pipeline/manifest.go:379 writeFileAtomic атомарно (temp+Sync+rename, pipeline/artifact.go:24-59) internal/runner/engine.go (tmctl manifest) → ingest.DecodeManifestinternal/books/parse.go, internal/readmodel и стало строже: Manifest.Whole() теперь зеркалит BookManifest.selfConsistent (pipeline/manifest.go:460-483) поле в поле по тем правилам, которые платформа вообще читает (главы, юниты, нумерация). Верхняя граница чанков движка у нас counterpart'а не имеет намеренно — платформа счётчиков чанков не берёт
    <project_db>.bank.json pipeline/bankexport.go:154 writeFileAtomic атомарно internal/runner/artifacts.go:37ingest.DecodeBankpgstore.SaveBank
    <project_db>.bank-stop.json pipeline/mining.go:393 writeFileAtomic атомарно читателя НЕТ (грепом по зоне — ноль вхождений) ⚠ канал существует и не потребляется; полная таблица подписи платформе сегодня не нужна, она проецирует банк
    <project_db>.bank-stop.txt pipeline/mining.go:195 os.WriteFile НЕ атомарно (усечение первым делом) читателя нет и это важно: брать его на живом прогоне нельзя — исправление пинга №15 оркестратором подтверждается кодом
    <project_db>.mined-signature.yaml pipeline/mining.go:188 os.WriteFile НЕ атомарно читателя нет то же
    <project_db>.auto-bank.yaml pipeline/mining.go:741 os.WriteFile НЕ атомарно читателя нет то же
    mined_delta (путь из book.yaml) писателя в движке НЕТ — только читатель pipeline/mining.go loadMinedDelta; формат seed.File (internal/seed/seed.go:63-80), грузится membank.LoadGlossarySeed, Source пере-штампуется на "mined" писателя НЕТ и у платформы разрыв — это и есть строка 199(а)
    mined_rejects читатель pipeline/mining.go loadMinedRejects; формат rejects: [{src, note}]; ФИЛЬТР ПРЕДЛОЖЕНИЙ, в банк не входит писателя нет тот же разрыв
    events.jsonl (NDJSON эмиттера) движок, StreamVersion 1.1 append-only internal/ingest/tail.go + pgstore.RunSink пак не трогал
    exit-коды tmctl контракт движка ingest.OutcomeOf, internal/runs/reconcile.go outcome пак не трогал
    tmctl status --json движок internal/runner/engine.goingest.StatusReport; расчёт денег пак не менял форму; изменил лишь то, СКОЛЬКО раз его зовут при неудаче (бэкофф)
    book.yaml оператор (шаблон) + платформа один раз (internal/books/render.go, O_EXCL) платформа ПИШЕТ пять фиксированных ключей декодер движка СТРОГИЙ (config/book.go:142-144, KnownFields(true)): незнакомый ключ = жёсткая ошибка. Значит любое расширение набора ключей платформой — ратификация, а не правка. Прямо относится к развилке 199(а) выше
  • P7, акт 5 — ЗАКРЫТ. Дерево готово к приёмке и лендингу. Батарея: 18 пакетов, exit 0, скипов 0, линтер 0 issues, тестовых функций 532 (⚠ было «533» — испр. оркестратором №18 20.08: канонической командой D39.121 grep -rh '^func Test' platform --include='*_test.go' | wc -l выходит 532; расхождение на единицу, ни на что не влияет, но число было заявлено без команды). Регистр — 327 строк, открытых 65 (13 minor, 52 info), major и BLOCKER нет. Дерево не закоммичено и не застейджено. Миграции пака: 0001900024.

    Что построено и зачем

    1. Долг на материализацию — колонка с арендой (books.read_model_owed_at, 00019). Ставится в ТОЙ ЖЕ транзакции, что закрывает границу работы (конец разбора и все три конца прогона), гасится сверкой метки, берётся в аренду тем, кто платит. Зачем: очередь жила в памяти процесса — упавший Refresh её терял, рестарт демона терял её всю, и оплаченный текст не доезжал до читателя НИКОГДА. Плюс «в покое» для потока теперь значит «и материализация не должна» — это Ф-56: книга, только что нарезанная, получала end и 204 «не переподключайся» ровно в те секунды, пока дерево ещё не легло. Почему отошёл от прежней реализации: удалены очередь в памяти (runs.deferRefresh/ DrainRefresh/Reader) и бэкстоп «дерева нет вовсе» (books.materializeMissingTrees, pgstore.BooksWithNoTree) — оба покрывались одним механизмом, а бэкстоп к тому же не видел книгу, у которой дерево есть, но на границу старше. Дрейн переехал в readmodel.Drain — в пакет, чья это работа.
    2. «Глава сделана» = последний проход, который книга на ЭТОМ деплое реально получает (books.edit_wave, 00024). Зачем: счёт шёл по волне edit, и на деплое без редактора ни одна глава не была сделана никогда — шкала покупки не подрезалась, сервис бесконечно предлагал купить уже переведённое. Форму объявляет сам движок (пайплайн без редактора даёт волне edit знаменатель ноль). Почему на КНИГЕ, а не на прогоне: прочитанная с последнего прогона, она роняла chapters_done в ноль в момент допуска нового прогона — счётчик ходил назад, что канон запрещает. Флаг монотонный: книга, прошедшая редактирующий пайплайн, остаётся такой.
    3. У клейма идемпотентности появился владелец (claim_token, 00020) и тождество интейка решается дайджестом файла (content_sha256, 00023). Зачем: строка ключа не несла, какая попытка её держит — застрявшая попытка удаляла строку преемника (пользователь получал вторую книгу) или писала свою квитанцию поверх чужой (клиент реплеил Location несуществующей книги); а тождество по Content-Length не различало две РАЗНЫЕ книги с одинаковым объявлением.
    4. Остановка АККАУНТА читается с аккаунта, а не с потолка одного прогона; resume различает «прогон закончен, нужен новый» (ceiling_reached) и «пополните счёт» (credit_unavailable); blocked называет холд, который реально освободит шкалу. Зачем: всё это — «у вас нет денег» пользователю, у которого деньги есть, и отправка его делать не то.
    5. Счётчик замечаний вернулся на главу (chapters.note_count, 00022), chapters.units_done снесён (00021). Почему это разворот решения 00016 того же пака: 00016 снёс колонку с доводом «её никто не пишет» — здесь она возвращается ВМЕСТЕ с писателем, которого ей не хватало. Основание замерено: джойн, которым её заменяли, стоил 83% времени страницы библиотеки (16.8 мс против 3.4 мс на корпусе 40 книг × 500 глав, ВАКУУМИРОВАННОМ); со счётчиком — 6.6 мс. ⚠ Условие замера названо здесь и при каждом другом числе про него (P8-FIX, PD-336): на ХОЛОДНОМ корпусе тот же джойн даёт 96%, и это не противоречие, а другой замер. Нормативный носитель вакуумированных цифр — миграция 00022, остальные на неё указывают. Бенчмарк оставлен в дереве: go test ./internal/pgstore/ -bench LibraryPage -run xxx.

    Что форсировано и почему

    • Работа по канону 0.4.0 лежит в дереве до его ратификации. Основание: ответ контрактной сессии на записку зоны плюс слово владельца 20.08 про «кончились деньги → сообщить и дать продолжить». Ломающая правка ровно одна — Run.stop_requested (0.3.0 такого поля не объявляет); cause.code: credit_unavailable легален и по 0.3.0, второй уровень там открыт. Зависимость записана строкой PD-327 и закрывается лендингом канона, а не работой зоны. Если форма сдвинется словом владельца — правки отделимы, карта в archive/P7_ACT5_FIX_PLAN_2026-08-20.md §5б.
    • Тесты на АТОМАРНОСТЬ сверяют xmin (транзакцию, последней писавшей строку). Это не экзотика ради экзотики: два пина акта проходили под мутацией «вынести метку из транзакции во второй оператор» — конечное состояние она не меняет, меняет окно, и утверждать про окно иначе тест не может.

    Что осталось, названо и не спрятано

    • PD-327 — ждёт лендинга канона 0.4.0 (не работа зоны).
    • PD-297 — round-trip на строку в SaveStructure под эксклюзивной блокировкой книги. Не взят осознанно: путь самый опасный на запись, инструмент штатный (tx.SendBatch), но правку надо мерить на корпусной книге до и после.
    • Четыре оси не смотрел никто и никогда: деньги и леджер целиком · вход/сессии/CSRF · очередь и джобы · метрики. Пак их не менял, а все приёмки смотрели дифф. Это отдельный пак.
    • Живого перевода на стенде не было ни разу — движку нужны ключи провайдера и деньги. Всё после «прогон стартовал и остановился» (подпись банка, halt на потолке, резюм после пополнения) покрыто батареей против живого Postgres, а не живым движком.

    Как это проверялось

    make check с гейтами (скипов 0) · каждая правка проверена ПОСАДКОЙ МУТАЦИИ: код испорчен именно так, как называет комментарий пина, пин обязан упасть, код возвращён (40 проб, все поймались) · живые пробы по ручкам на стенде: интейк и реплей, 409 key_reused на другой файл под тем же ключом, часть после файла на реплее, /chapters /units /notes /bank, ETag→304 и разные теги у страниц, 410 на чужой id главы, after_version из прошлой жизни, unsupported_pair, /usage на пустом счету и после adjust, старт и стоп прогона со stop_requested на проводе, SSE в покое и SSE с непогашенным долгом (Ф-56 на проводе), 400 вместо 413 на длинном теле.

    Самопроверка

    Акт закрыт адверсариальным ревью СОБСТВЕННОГО диффа (9 линз × 2 рефутера + критик полноты, 84 агента, 0 ошибок): 37 находок, 18 пережили рефутеров, и почти все — дефекты, внесённые правками самого акта. Строки PD-315…PD-327, каждая с механизмом и пином. Два из них — собственные пины акта, проходившие под мутацией, которую сами называют (PD-318): это третье появление класса в паке, и он назван в плане §3 п.2 ещё до того, как повторился.

  • P7, акт 5 (начало) — исполнение фиксов по ревью акта 4 + ответ контрактной сессии. План и рабочий список — platform/docs/archive/P7_ACT5_FIX_PLAN_2026-08-20.md (читать первым; §5а — что пришло из контракта). ⚠ Контрактная сессия ответила 20.08 на записку зоны archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md: канон стал 0.4.0 (лежит в дереве контрактной зоны, НЕ ратифицирован — до лендинга в силе 0.3.0). Все три вопроса, висевшие на владельце, закрыты. Зоне пришла работа: resume исчерпанного прогона — 409 с двумя РАЗНЫМИ причинами (ceiling_reached против нового credit_unavailable); «глава сделана» = последний реально пройденный проход, и это ДЕНЬГИ — ChaptersLeft на деплое без волны редактора не подрезается вовсе, сервис бесконечно предлагает купить уже переведённое; отпечаток интейка — по содержимому, Content-Length из нормы вышел, а невозможность установить тождество обязана давать 409, а не реплей; Run.stop_requested обязательным булевым (единственная ломающая правка). Отклонены: слово под суточный потолок движка (решение владельца 15.08) и предложенный зоной признак «прогон продолжаем» (три лишних чтения на карточке, которую перечитывают на каждый кадр). Акт 4 закрылся адверсариальным ревью СОБСТВЕННОГО диффа (9 линз × 2 рефутера, 75 агентов): 33 находки, 27 пережили рефутеров, и часть из них внесли сами правки акта 4. Тяжелейшая — четыре независимые линзы сошлись на том, что реконсилятор снимал стоп подписи банка, которого никто не подписывал, и доспавнивал движок без --verify-bank: складывая снятие стопа внутрь общего reopen, акт 4 не проверил, что reopen зовёт не только resume, но и свип. Нарушение согласия пользователя; починено ключом на вызывающего, пин проверен посадкой мутации. ⚠ Первый написанный на это пин ПРОШЁЛ под мутацией — фикстура не доходила до кода, потому что часы сервиса захвачены при конструировании и f.now их не двигает. Урок в плане, §3. Закрыто также: загрузочная проверка пар — на ДОСТУПНОЙ половине (деплой с одними :unavailable больше не стартует), интейк не трактует пустой список как «пускать всё», теги сравниваются регистронезависимо. Батарея: 18 пакетов, exit 0, скипов 0, тестов 503.

  • P7, акт 4 — доработка 20.08: сверка ВСЕХ находок против дерева + триаж §8.2. (Пакет релеев archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md §7; три вопроса владельцу §8.1а; вопросы контракта вынесены в platform/docs/archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md для отдельной контрактной сессии.) Все 74 позиции обеих приёмок сверены поштучно с ТЕКУЩИМ деревом — каждая с цитатой живого кода, исход «не проверено» отдельно от «опровергнуто». ⚠ Три места числились закрытыми и закрыты не были — правка не закрывала собственный сценарий: (1) подрезка буфера потока сверялась ПОСЛЕ того, как водяной знак перепрыгнул дыру, поэтому условие не срабатывало никогда и кадры note терялись молча (PD-283); (2) CompleteIdempotency писала квитанцию в строку, которая попытке уже не принадлежит — воспроизведено живым PG, клиент получал квитанцию ЧУЖОГО запроса (PD-284); (3) пин на снятие --verify-bank был ПУСТЫМ: фикстура стартовала прогон без stop_for_signing (PD-285). Все три «подтверждались» зелёной батареей. Ещё закрыто: HEAD на потоке больше не держит горутину и ошибка Flush не выдаётся за отправленный кадр (PD-286) · blocked называет чужую книгу только когда её холд укорачивает шкалу (PD-287, пина не было вовсе) · note_count и GET /notes описывают одно множество (PD-288) · устаревшая доставка не рождает кадр (PD-289) · чтения на одном снапшоте (PD-290) · флаг «текст прочитан» переехал на ПАРУ (PD-291) · снятие стопа банка внутри транзакции переоткрытия, отдельного писателя больше нет (PD-292) · проход материализации подрезается своим бюджетом (PD-293) · блокировка книги в решениях банка (PD-294) · дубликат индекса снят (PD-295) · маршруты и коды ошибок — по ОДНОЙ таблице, тесты ходят по ней (PD-296) · бэкстоп-свип для книги без дерева (PD-276 дозакрыт) · интейк не платит за третью ре-нарезку (PD-248 актуализирован). Триаж §8.2: 17 сделано, 5 отклонено с причиной, 1 релей. Батарея: 18 пакетов, exit 0, скипов 0, линтер 0 issues, тестов 458 → 501; каждая правка проверена посадкой мутации, не рассуждением. ⚠ Осознанно НЕ сделано, с причиной: PD-297 (round-trip на строку под блокировкой книги — мерить прежде правки) · PD-202 (draft-only, вопрос владельцу) · PD-282 (канон §resumeRun не покрывает «денег не осталось») · PD-298 (сперва сверка у движка) · PD-299. Осей, которых не смотрел никто: деньги и леджер · вход/сессии/CSRF · очередь и джобы · метрики.

  • P7: приёмка → правки → приёмка правок (17.08). ⚠ Полный хендофф под сессию без контекста — platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md (читать §0§3, дальше по нужде). Там же: принципы приёмки, сверка с исходным промтом, техдолг, план следующей сессии. Категорийное ревью по 12 осям с адверсариальной верификацией: 80 находок, 54 подтверждено, 16 правдоподобно, 10 опровергнуто; после дедупа 46 уникальных дефектов — 3 блокирующих класса, 15 HIGH, 17 MEDIUM. Батарея была зелёная, потому что все три блокирующих дефекта живут в путях, которые тесты проходили только в безопасной форме. Починено в зоне (регистр PD-257…PD-281): пере-нарезка книги больше не валит читающую модель навсегда (отложенный констрейнт, миграция 00018) · упавший tmctl export больше не затирает текст пустотой · весь блок идемпотентности (гонка первых попыток 500→409, область ключа, отпечаток с размером файла, закрытие ключа вне контекста запроса) · база полосы прогресса считает тот же проход, что числитель · SSE (подрезанные кадры → resync_required, дедлайн записи, id из будущего) · материализация не голодает свип · банк читается на штатном деплое · деплой-нота больше не включает обратно грант, который PD-104 выключил. Батарея: 18 пакетов, exit 0, скипов 0, линтер 0 issues, тестов 458 → 490. ⚠ Приёмка ПРАВОК (9 осей + fable-5) нашла, что восемь дефектов внесли сами правки — батарея не увидела ни одного, три были запинены под ошибку автора. Все исправлены; разбор — хендофф §0. Б-1 ПОЧИНЕН разбором с владельцем 17.08 (PD-277). Рамка приёмки была неверна: это не «нужен новый канал в движок», а невыполненная половина строки единого бэклога 191(в) (слово владельца 16.08, D39.144) — «проводка resume = снятие стопа до движка». Движок при выключенном --verify-bank уже делает модель владельца: неподписанное едет авто-строками с пометкой ⟨проверить⟩ (mining.go:211-231, D39.42 п.3), а платформа передавала флаг обратно и роняла прогон в тот же стоп. Снято: l.VerifyBank && !l.BankReleased; пин TestAResumedRunIsSpawnedWithoutTheSigningStop. ⚠ Остаток: decline пользователя до движка не доезжает (движок читает mined_rejects) — строка бэклога 192, отложена владельцем. PD-273 закрыт решением владельца 17.08: статус остаётся awaiting_bank, лечение — признак «стоп был запрошен» в контракте; готовая строка для единого бэклога — хендофф §7(и), вставляет оркестратор (зона docs/ не наша). Разбор, план, обязательства через оркестратора и команды воспроизведения — platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md. ⚠ Заявки раздела «Сессия P7» ниже приёмку НЕ прошли — читать их вместе с хендоффом, не вместо.

  • P7 ПОСТРОЕН — заявка САМОЙ сессии написания (17.08).Приёмку эта заявка НЕ прошла — см. верхнюю запись и archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md: 46 уникальных дефектов, три блокирующих класса, из них ни один батарея не видела. Читать ниже как исходную позицию, а не как состояние дерева. Читающая поверхность контракта 0.3.0: главы · пары с текстом · замечания · банк и подпись · SSE на КНИГЕ · GET /capabilities · машинная модель ошибок (code + request_id) · условные чтения и сжатие · Idempotency-Key · structure_version. Плюс форварды (title, reject_reason), PD-104 (грант → 0), демонтаж построенного гейта полноты банка (D39.144), PD-241, конформность построенного по §5 промта и рантбук деплоя, прогнанный ЖИВЫМ migrate. Батарея на стенде ~/.local/pgsql (порт 55433) под -race с ОБОИМИ гейтами: EXIT=0, 18 пакетов, скипов 0, линтер 0 issues, тестовых функций 416 → 458. Дифф: 46 файлов, +2329/646. Селф-ревью финалом — ТРИ независимых ревьюера, два из них другой модели: 22 находки, 17 подтверждены и починены (две HIGH нашли оба контрактных ревьюера независимо), 5 — диспозиция с аргументом. Разделы ниже — записка-план, живые пробы, таблица селф-ревью, obstacle, вопросы оркестратору.

  • Запись оркестратора, 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/1019 · 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 1114.08, принят D39.130; промт с баннером-исходом — archive/PLATFORM_P5_SESSION_PROMPT_2026-08-10.md.) Эскроу/uncertain (строка 136) — следующим денежным промтом, НЕ в P5.

  • Эры P0P3 (вход OIDC · кредиты · админ-CLI · деплой · фикс-паки) — исполнены и залендены (D39.107/109/112/114); разделы — в archive/platform-PROGRESS-P0-P3.md. Стек и рецепт стенда — STACK_DECISIONS.md; критерии приёмки — ENGINEERING_STANDARDS.md.

(Записи паков P4P6 с дофиксами (0815.08) — в archive/platform-PROGRESS-P4-P6.md; вынесено 16.08 по слову владельца.)

Эра пака P7 (1620.08) — в архиве

Пять актов, приёмка, фикс-раунды и таблицы селф-ревью выселены срезом в archive/platform-PROGRESS-P7.md (норма: закрытая эра не живёт в журнале). Итог и то, что пережило пак, — шапка выше и пинг оркестратора №18 ниже.

Закрытые эры P0P3 — в архиве

Разделы сессий P0P3 и их ратификаций (0408.08) вынесены в archive/platform-PROGRESS-P0-P3.md (D39.124). Решения оттуда живут в D-логе и DEFECT_REGISTER.md.

Пинги оркестратора (живые ссылки для следующих сессий)

Пинг оркестратора №15 (09.08, D39.122): движковый пак «блокеры контракта» ПРИНЯТ и заленден 0e69bc1 — пять поверхностей для платформы существуют. ФИНАЛЬНЫЕ формы (менялись трижды за приёмку — старые в переписке игнорировать):

  • Манифест глав: <project_db>.manifest.json, manifest_version: "tm-manifest-v2" (v1 движок сам отклоняет); id главы = 16 hex (стабилен через пере-нарезку); unit.id = <chapterID>:<cutTag>:<firstChunkIdx> — тег разреза 8 hex, при любой смене нарезки/данных пары unit.id УМИРАЮТ намеренно (id жив ⇒ якорь цел); поле heading — ВРЕМЕННЫЙ рендер движка «Глава N», НЕ метка книги (решение владельца 09.08; настоящие заголовки — строка 160 бэклога движка). $0-команда tmctl manifest строит дерево до первого прогона (первое касание создаёт БД проекта — то же поведение, что у status).
  • Прогресс: status --jsonprogress: {draft:{done,total}, edit:{done,total}} на книге и в каждом элементе chapters; done = «разрешено волной» (ok/flagged/skipped); волна, которой нет, — total: 0; процент не отгружается — собирает клиент.
  • Банк: <project_db>.bank.json (весь банк тремя статусами; id термов — длино-префиксированный хеш ключа уникальности, стабилен через пересборку) и <project_db>.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 <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): SignupGrantMicroUSD0 на бете, начисление руками; возврат $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:189sweepBudget = 2 * time.Minute на ВЕСЬ проход; internal/runs/reconcile.go:70defaultRunBudget = 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, он же в каждом hellostream.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_signingreadmodel.go:441,446, там же note_count и сломанный алиас b), куда sqlc по построению не дойдёт. Тем же паком — гейт, который туда дойдёт: прогонять КАЖДЫЙ собранный запрос через разбор Postgres (prepare/describe) против мигрированной схемы; склейка ему не мешает, он получает финальную строку. И ответить попутно на вопрос, которого мы не знаем: есть ли в read-модели запрос, которого не касается ни один тест — если есть, это не «медленная обратная связь», а дыра. ⚠ Половина ответа УЖЕ получена вторым рубежом приёмки (собранный SQL извлечён из пакета через go/types и прогнан EXPLAIN (GENERIC_PLAN) против мигрированной схемы): 141 запрос, все планируются чисто, остаточных «нет такой колонки» НЕТ; тестом ЭТОГО пакета недостижимы десять, но по батарее ЦЕЛИКОМ реально не покрыты только три (DeleteOldLoginEvents · UserByIdentity · Observe; остальные семь покрыты на 5887% тестами соседних пакетов — пере-мерено покрытием при аудите 20.08, первая редакция называла десять и это было неверно) — RefundParseAttempt · StuckIntake · OpenReservations · ReleaseUnspawned · DeleteOldLoginEvents · UserByIdentity · Observe · SpendBound · AttemptReservationOpen · RunPausedReason, и ни один из них не трогает схему 0001600024. То есть дыры сегодня нет, а гейт нужен как ПОСТОЯННЫЙ — именно он и делает этот ответ воспроизводимым.

  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.jsonbackend/internal/pipeline/artifact.go:24-59); bank-stop.txt, mined-signature.yaml, auto-bank.yaml пишутся os.WriteFile с усечением. Сегодня безвредно (читателей нет), но сессия, взявшая любой из них по той строке, получит усечённый документ на живом прогоне.

Сессия P7 ЗАКРЫТА владельцем 20.08. Промт отработан → platform/docs/archive/. Дальше — другие сессии.