62 KiB
Слайс зонного журнала: эра пака P8-FIX (21–22.08.2026)
⚠ Архив. Инструкции отсюда НЕ исполнять, при онбординге НЕ читать — только по конкретной ссылке. Вынесено оркестратором №18 22.08 при актуализации доков. Состояние зоны — шапка живого
../platform-PROGRESS.md; дефекты —../DEFECT_REGISTER.md; правила, пережившие пак, —../STACK_DECISIONS.md; ратификация — D39.154 вdocs/architecture/05-decisions-log.md.Что этот пак сделал: закрыл фикс-лист приёмки P7 (пункты 1–7 и 9–13) и врезанный первым блокер свипа
PD-169— голодание, из-за которого расчёт денег вставал для ВСЕЙ инсталляции. Плюс спил пер-термного write-пути банка по слову владельца (PD-370, ратификация D39.144). Лендинг —31f1f82.⚠ Что из этого отчёта ПЕРЕНЕСЕНО в долговечные носители и там первично: инвентарь каналов шва с движком →
../STACK_DECISIONS.md§«Инвентарь каналов движка» (и там он ПОЛНЕЕ: дописан канал текстаtmctl export --json --pairs, которого в этой таблице не было) · развилка владенияbook.yamlс четырьмя вариантами → строка 199(а) единого бэклога · поимённый разбор канона под минор 0.5.0 → строка 206 · граница sqlc (41 запрос) →BACKLOG.mdП-19 · ловушка.check.logи третий гейт батареи →PD-374.
-
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;--lintexit 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/П-21—ClaimIdempotencyпод конкуренцией отвечает 500 (1–2 падения на ~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. ContractVersion0.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, строка бэклога П-195. PD-297(round-trip под блокировкой)отказ с причиной нечем и не на чем мерить — ниже 6. Труба доставки решений банка (строка 199а) не строил, сдаю вопросом — как промт и требует ниже, отдельным разделом 7. Мусор: задвоенная строка доккомментария исполнен internal/books/books.gocanTranslate(PD-335)8. Ревью-пак четырёх осей НЕ мой — заказ пака P8-REVIEW — 9. Один носитель на факт для замера 00016→00022 исполнен, с названным расхождением с буквой пинга internal/pgstore/perf_test.go(PD-336)10. Пол на пустой манифест у материализатора исполнен internal/ingest/manifest.goWhole(),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— единственный ретрай отложенного расчёта — не вызывается ВООБЩЕ, для всей инсталляции, каждый проход. Построено:- Проход разделён на фазы. Реконсиляция получает половину остатка, расчёт — свой кусок
(
runs.phaseBudget). Выбранный бюджет фазы больше не ошибка свипа: очередь просто ждёт тика. - Отсрочка с растущим бэкоффом на ПОПЫТКЕ (
run_attempts.reconcile_after, миграция 00025). Свип читаетRunsToReconcile, а неListLiveRuns; порядок «старейший первым» не тронут — он верен для всего, что просто медленно, — а из ГОЛОВЫ заклиненный прогон убирает именно отсрочка. Телеметрия (Lag) по-прежнему видит всё. - Терминальное состояние после N неудач (
runs.StalledAfter= 5, ≈ четверть часа попыток): прогон считается ЗАСТРЯВШИМ, гейджtm_platform_runs_stalled, одна ERROR-строка на пересечении порога — не на каждом тике. - Ручка у ОПЕРАТОРА, которой не было вовсе:
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.goReady, где имя таблицы принадлежит 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), тогда как 80–83% — вакуумированный. Носители мерили разное и все честны.Строка 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.goloadMinedDelta: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.go—O_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.gominedRejectFile), это ФИЛЬТР ПРЕДЛОЖЕНИЙ, в банк не входитна стороне платформы решения write-only internal/pgstore/readmodel.goSubmitBankDecisionsпишет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_BUDGET2m0sdefault,TM_PLATFORM_RUN_BUDGET1m0sdefault) — норма 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) и компаньон:672(«POST /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 и починки их находок — воркфлоу пошире, 10–30 агентов, один 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.mdgained 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) + рефутер на каждую находку:Линза (агент) Предмет Цитата или команда ИЗ ЕГО отчёта Находок Пережило a2fdcd13144058b57Fable 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 ada2420ac7c576856CLI, метрики, конфиг «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.jsonpipeline/manifest.go:379writeFileAtomicатомарно (temp+ Sync+rename,pipeline/artifact.go:24-59)internal/runner/engine.go(tmctl manifest) →ingest.DecodeManifest→internal/books/parse.go,internal/readmodel✅ и стало строже: Manifest.Whole()теперь зеркалитBookManifest.selfConsistent(pipeline/manifest.go:460-483) поле в поле по тем правилам, которые платформа вообще читает (главы, юниты, нумерация). Верхняя граница чанков движка у нас counterpart'а не имеет намеренно — платформа счётчиков чанков не берёт<project_db>.bank.jsonpipeline/bankexport.go:154writeFileAtomicатомарно internal/runner/artifacts.go:37→ingest.DecodeBank→pgstore.SaveBank✅ <project_db>.bank-stop.jsonpipeline/mining.go:393writeFileAtomicатомарно читателя НЕТ (грепом по зоне — ноль вхождений) ⚠ канал существует и не потребляется; полная таблица подписи платформе сегодня не нужна, она проецирует банк <project_db>.bank-stop.txtpipeline/mining.go:195os.WriteFileНЕ атомарно (усечение первым делом) читателя нет ✅ и это важно: брать его на живом прогоне нельзя — исправление пинга №15 оркестратором подтверждается кодом <project_db>.mined-signature.yamlpipeline/mining.go:188os.WriteFileНЕ атомарно читателя нет ✅ то же <project_db>.auto-bank.yamlpipeline/mining.go:741os.WriteFileНЕ атомарно читателя нет ✅ то же mined_delta(путь изbook.yaml)писателя в движке НЕТ — только читатель pipeline/mining.goloadMinedDelta; форматseed.File(internal/seed/seed.go:63-80), грузитсяmembank.LoadGlossarySeed,Sourceпере-штампуется на"mined"— писателя НЕТ и у платформы ❌ разрыв — это и есть строка 199(а) mined_rejectsчитатель pipeline/mining.goloadMinedRejects; форматrejects: [{src, note}]; ФИЛЬТР ПРЕДЛОЖЕНИЙ, в банк не входит— писателя нет ❌ тот же разрыв events.jsonl(NDJSON эмиттера)движок, StreamVersion 1.1 append-only internal/ingest/tail.go+pgstore.RunSink✅ пак не трогал exit-коды tmctlконтракт движка — ingest.OutcomeOf,internal/runs/reconcile.gooutcome✅ пак не трогал tmctl status --jsonдвижок — internal/runner/engine.go→ingest.StatusReport; расчёт денег✅ пак не менял форму; изменил лишь то, СКОЛЬКО раз его зовут при неудаче (бэкофф) book.yamlоператор (шаблон) + платформа один раз ( internal/books/render.go,O_EXCL)— платформа ПИШЕТ пять фиксированных ключей ⚠ декодер движка СТРОГИЙ ( config/book.go:142-144,KnownFields(true)): незнакомый ключ = жёсткая ошибка. Значит любое расширение набора ключей платформой — ратификация, а не правка. Прямо относится к развилке 199(а) выше - Проход разделён на фазы. Реконсиляция получает половину остатка, расчёт — свой кусок
(