textmachine/platform/docs/platform-PROGRESS.md

907 lines
165 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# Журнал зоны «Платформа»
> Весь прогресс платформы — ЗДЕСЬ (решение владельца 04.08): пинги, итоги сессий, открытые
> вопросы, предложения на ратификацию. В `docs/PROGRESS.md` платформа не пишет; оркестратор
> читает этот журнал при каждом лендинге зоны (свип «решений владельца» — норма D39.99 п.4).
## ФИКС-РАУНД ПО ДИСПОЗИЦИЯМ ОРКЕСТРАТОРА ИСПОЛНЕН — 13 фиксов, 9/9 посадок пойманы, одна находка воркфлоу ОПРОВЕРГНУТА исполнением, канон 0.6.0 принят (сессия платформы, 28.08, после записи ниже)
Диспозиции пришли двумя сообщениями оркестратора (28.08) + третьим — канонная половина полосы
(0.6.0). Всё исполнено, кроме ДВУХ пунктов с несогласием (аргументы ниже — обе позиции по норме
«несогласие говори»).
### Фиксы в дереве (сверх сданного пака; каждый с пином, посадки — в копии `~/tm-p9-mut2`)
| # | Что | Пин | Посадка |
|---|---|---|---|
| F1 | **MAJOR(а) 1 МиБ**: `ingest.EncodeDecisions` рендерит с `SetEscapeHTML(false)` (документ читает движок, не браузер; выбор обоснован в комментарии) **+ жёсткий гейт**: рендер сверяется с зеркалом движкового капа `ingest.MaxDecisionsDocument` ДО спавна → `ErrBankDocumentTooLarge`**413** (остаточная конвертная полоса ~40 байт закрыта гейтом, слово всегда канонное) | `TestTheRenderedDocumentDoesNotInflateEscapableBytes` (ingest) · `TestARenderedDocumentOverTheEngineCapIsRefusedBeforeTheSpawn` (runs, движок НЕ спавнится) · `TestARenderedDocumentOverTheEngineCapAnswers413` (httpapi) | M6 (возврат `json.Marshal`) и M7 (снятие гейта) пойманы топично |
| F2 | **MAJOR(б) мьютекс**: `lockBook(ctx)` — одноместный канал вместо `sync.Mutex`, ожидание наблюдает контекст; **бюджет двери ставится ДО очереди** (накрывает ожидание+вызов); Start/Resume передают свои ctx; отмена в очереди корректно декрементит refcount | `TestAWaiterWhoseContextEndsLeavesTheBookQueue` (+ leak-тест расширен) | M11 (ожидание игнорирует ctx) поймана |
| F3 | **Р1 окно стопа**: `ReadBookForRun` вырос полем `LiveRunAwaitingBank` (живая строка в `awaiting_bank`); предикат двери — `HasLiveRun && !LiveRunAwaitingBank`; Start по-прежнему держится на `HasLiveRun` (вторая строка сломала бы `runs_one_live_per_book`); худший случай окна — движок ещё дожёвывает → честный класс 12 → 503 «повтори». Довод в комментарии переписан, противоречие с `reconcile.go:1122` разрешено в пользу кода | `TestTheDoorOpensOnTheSigningStopWindow` (фикстура — как `control_test`: журнальный `awaiting_bank` без `finished_at`); обратная сторона держится старым `TestCorrectionsRefuseWhileTheBookIsBeingTranslated` | M8 (старый предикат) поймана — refuse-тест при этом остался зелёным |
| F4 | **Р2 миграция 00026**: два UPDATE сведены к ОДНОМУ — `draft_before := chapters_before` для ВСЕХ строк (та аппроксимация, которую ревью само проверило как самосогласованную для finished): пере-снятие БАЗ ПОСЛЕ работы прогона — единственный источник вечного недоезда 2·p_e — удалено; заодно умер и dispute про READ COMMITTED между двумя стейтментами (стейтмент один). Остаток по эпохам старого `chapters_before` честно назван в комментарии миграции. `migrations.sha256` пере-подписан (миграция не релизнута — слово оркестратора) | пин исполнением невозможен (тестовые БД наливаются миграциями ДО данных — старая семантика юнитом задним числом непроверяема); держится формулой + комментарием | — (названо, не скрыто) |
| F5 | **Р5 presence-vs-null**: все строковые члены `wireCorrection``nullableString` (парный `nullableInt`); явный `null` на любом = malformed ТИПА (рефьюз до правил присутствия); правила присутствия (`id`×tuple, `dst`/`kind` на decline) считают КЛЮЧИ | +3 кейса в `TestACorrectionRequestIsValidatedWholeWithPointers` (null-src при id · null-dst на decline · null-action) | M10 (снятие null-гейта) поймана |
| F6 | **Р6(i)**: комментарий SIGKILL-ветки переписан честно (ветка достижима только когда SIGTERM НЕ отработал; полу-приземлённая пара без отчёта — остаток в PD-407) | — (комментарий) | — |
| F7 | **Р6(ii)**: `bankStopGrace` 10 с**30 с**, число обосновано замером движка в комментарии (11.3 с непрерываемого фолда на документе-максимуме, ×2.5 запас на медленный хост) | — (константа с доводом) | — |
| F8 | **Р6(iii)**: связка «ручка `TM_PLATFORM_RUN_BUDGET` × движковый кап 5000×11.3с» и цена понижения названы комментарием у бюджета двери (механизм не строился — слово оркестратора) | — | — |
| F9 | **Р7**: `Service.SweepCorrectionScratch()` — подметание `bank-corrections-*.json` на буте (вызов в композиционном корне `tmplatformd` ДО подъёма HTTP), файлы вне маски не трогаются | `TestBootSweepsOrphanedCorrectionDocuments`. ⚠ Честно: вызов ИЗ main юнитом не запинен — мутация «не звать на буте» ловится только чтением | M14 (маска мимо) поймана |
| F10 | **Д5**: комментарий `spawn.go` о сбросе `--verify-bank` переписан с упразднённого D39.144-контракта на настоящее основание (память v16 покрывает карту; старое основание питало дырявый гард — названо в комментарии) | — | — |
| F11 | **Н5 hello**: при `resuming` hello несёт `last` клиента, не голову истории (свежий коннект — голову, как и `from` четырьмя строками ниже) | `TestAResumingHelloCarriesTheClientsOwnWatermark` (+ свежий коннект отдаёт голову) | M13 поймана |
| F12 | **Н1-гард**: `Resume` под мьютексом сверяется с новым `pgstore.LatestRunID` (тот же порядок, что `lastRun`) — не-последний прогон получает `ErrNotResumable` с человеческим доводом; реконсилерский RestartRun в гарде не нуждается (живой прогон всегда последний по `started_at`: пока он жив, новый не стартует) | `TestAnOlderRunCannotBeResumedOverANewerOne` (Now сдвигается между стартами — фикстурный Now дал бы одинаковый `started_at` и флейк по случайному id) | M12 поймана |
| F13 | **Канон 0.6.0** (третье сообщение): `ContractVersion` → 0.6.0; комментарий `wireProgress` переписан (сквозная доля и `stage` теперь КАНОННЫ, открытый словарь; оба условия исключения шапки — вывод платформой из тех же счётчиков и открытость — коду отвечают, проверила); протухший абзац про `stop_requested` заменён на «ни одного члена впереди канона»; комментарий у теста строки библиотеки сужен до правды (сам тест не тронут) | гейт версии (`reading_test`) снова зелёный | — |
### Два НЕСОГЛАСИЯ с диспозициями (норма «говори»)
1. **Р4 (`limitedBuffer` не убивает чайлда) — находка воркфлоу ОПРОВЕРГНУТА исполнением, фикс
ОТКАЧЕН.** Посадка M9 (kill вырезан) прошла пин за **0.09 с** — чайлд умер сам; исходник Go
называет механизм прямо: копирующая горутина exec закрывает читающий конец при ошибке Write
(`os/exec/exec.go` writerDescriptor: «pr.Close() // in case io.Copy stopped due to write
error») → EPIPE на следующей записи. Агент экстраполировал семантику `StdoutPipe` (там `drain()`
Kill действительно нужен — exec ничего не закрывает за ручного читателя) на `Stdout=io.Writer`
и НЕ исполнял. Мой добавленный было kill снесён (он был бы кодом под ложный довод); исходный
комментарий `limitedBuffer` был ВЕРЕН и расширен ссылкой на опровержение; поведение запинено
живьём: `TestAnEndlessBankApplyIsRefusedRatherThanRead` (0.1 с; посадка M9b «кап перестал
отказывать» валит его таймаутом). Это второй случай за ревью, где замер бьёт рассуждение — в
обе стороны.
2. **Р2-dispute (READ COMMITTED между двумя UPDATE миграции) — строкой НЕ заведён:** фикс F4 свёл
миграцию к одному стейтменту, окна больше не существует.
### Регистр и якоря
- `PD-400.2` пере-описана честно (снято «ни ложного слова», мульти-репличный суб-кейс с ложным
503 назван прямо; оговорка внесена и в комментарий ветки класса 12) — редакция для акта лендинга.
- Новые строки: **PD-402..PD-406, PD-410** (major: resume не-последнего [read-половина, write-гард
закрыт F12] · sticky `edit_wave`/re-sell · `chapters_done` назад · бездеревная книга С ЗАМЕРОМ
достижимости 0.018 с из лога стенда · SSE hello [закрыта F11 — оркестратору решить статус] ·
Р3-полоса/план движка — АРХИТЕКТУРНОЕ, отдельным паком), **PD-407, PD-409, PD-411..413** (minor:
SIGKILL-слово+грейс [грейс закрыт F7] · утечка temp-файлов [закрыта F9] · мёртвые колонки классом
A второго яруса онтологии-18 · карточка 5 сканов · emitProgress под блокировкой), **PD-408**
(info: ручки двери). Статусы строк, чьё лечение легло этим раундом (405-замер/406/407-часть/409),
НЕ переводила — закрытие актом лендинга, как у PD-401.
- Гейты регистра: `counts.py --check` → **413 строк, 109 открытых (9/33/67), битая форма [], хвост
[]**. Якоря, сдвинутые МОИМИ правками, пере-нацелены (PD-375 `runs.go:269`, PD-*` books.go:1065`).
⚠ Для оркестратора: `docs/PROGRESS.md:159-160` и `17-seam-inbound-law.md:59` /
`25-seam-cold-review.md:7` держат якоря, уехавшие НЕ моими правками (supervisor/book.go — чужие
сдвиги) и моей (reconcile.go:1228 → теперь `:1246`) — файлы твоей зоны, чинить тебе.
### Числа финальной батареи фикс-раунда (каждое — командой)
`go test ./... -race -count=1 -v` с тремя гейтами → **EXIT=0, 18 пакетов ok, `grep -c -- '--- SKIP'`
→ 0, `grep -c '^=== RUN'` → 793** (было 781 на сдаче ревью — +12 новых пинов), `FAIL|DATA RACE` — 0
вхождений; `golangci-lint run`**0 issues**; `gofmt -l` пусто; `go vet` чисто (в составе линта).
⚠ Промежуточная батарея №2 имела РОВНО ОДИН честный FAIL — старый пин
`TestResumeIsRefusedWhenTheBookHasAnotherLiveRun` поймал, что первый вариант гарда F12 перекрывал
канонное слово `run_in_flight` при живом чужом прогоне; чинился КОД (словоразделение: живой сосед →
`run_in_flight`, финишировавший → `ErrNotResumable`), тест не тронут. Опись дерева:
`git status --short -- platform/`**50 путей (39 M + 11 ??)**.
## ВОРКФЛОУ-РЕВЬЮ ДЕРЕВА P9 ОТРАБОТАНО — 16 линз, оба отложенных MAJOR подтверждены замером, сводка находок для оркестратора (сессия платформы, 28.08)
Заказ владельца (релей 28.08): адверсариальная вычитка дерева воркфлоу-оркестрацией — полоса ×4,
дверь ×3, миграция, раскладка кодов (`wf_cac14b2f-e84`) + гонки данных по 4 траекториям владельца,
баг-хант нового кода, стоимость per-request (`wf_155de7c3-bb4`). Раскладка моделей — по слову
владельца: 1×Fable на воркфлоу (самая тяжёлая линза), остальным явный opus/sonnet. 16/16 агентов
дошли (0 ошибок), ~2.83M токенов. Мандат: «найди, где рассуждение неверно», каждая находка —
severity + file:line + траектория с числами; PD-281, PD-401-остаток и пере-нарезка исключены
заданием как названные границы. **По находкам НИЧЕГО не чинил — жду слова оркестратора.**
Дисциплина честности: пометка «подтверждено прогоном» в траекториях — исполнение АГЕНТОВ (их
overlay-тесты и /tmp-замеры в копиях дерева), не моё; я пере-исполнил чтением два клейма своего кода
(Р4, Р5 ниже — оба подтвердились) и одним своим EXPLAIN-замером ось стоимости (сошлось, ниже).
Полные результаты с траекториями сохранены вне репо: журналы воркфлоу в каталоге сессии
(`subagents/workflows/wf_*/journal.jsonl`).
### Два отложенных MAJOR ревьюера приёмки — оба ПОДТВЕРЖДЕНЫ, чинить
- **MAJOR(а) «1 МиБ на двух документах» — ПОДТВЕРЖДЁН тремя агентами с независимыми замерами.**
Механизм: `json.Marshal` в `ingest.EncodeDecisions` HTML-экранирует `&`/`<`/`>` (1 байт → 6) и
меняет конверт (+~25 Б), так что тело под проводным капом 1 МиБ рендерится в документ НАД
движковым капом 1 МиБ. Замер агента: 5000 declines с `note` из `&`×160 → тело 1 030 050 Б →
рендер 5 030 075 Б → движковый exit 14 → **409 `bank_corrections_refused` «split it» вместо
канонного 413**. Полоса законных тел широкая (~550 КБ плотного спецсимволами тела уже
перескакивает), а существующий пин `TestAnOversizedCorrectionDocumentAnswers413` дыру не ловит —
набит кириллицей, которая НЕ экранируется. ⚠ Конфликт линз: два агента первого воркфлоу положили
эту ось в «не опровергнуто», рассуждая от omitempty («рендер меньше тела») и НЕ меряя; замер двух
других бьёт рассуждение (один из «опровергателей» сам оговорил условие плотности `&<>`).
Бонус-cosmetic туда же: скраб пути (`runs/bank.go:146`) на этом отказе даёт задвоенную фразу «the
decision document the decision document» и пропускает на провод движковый префикс `pipeline:`.
- **MAJOR(б) «мьютекс без контекста, бюджет после захвата» — ПОДТВЕРЖДЁН** (`runs/bank.go:94` vs
`:126`, `lockBook` без ctx `:270-291`): K одинаковых ретраев дают очередь K×60 с; отменённый
клиентом запрос ЖДЁТ в очереди и, дождавшись, делает работу (чтение книги, temp-файл) за мёртвый
запрос; в ту же очередь встают Start и Resume — заявление «ждать один вызов движка»
(`runs.go:242-246`) держится только для одного ожидающего. Дёшево лечится ожиданием с
наблюдением ctx (канал вместо `sync.Mutex`).
### Находки в МОЁМ коде P9 (сверх двух MAJOR; лечение — после слова оркестратора)
| # | Вес | Что | Где |
|---|---|---|---|
| Р1 | breaks | **Окно стопа подписи**: `bank_stop` ставит `awaiting_bank` БЕЗ `finished_at`, а `HasLiveRun` читает `finished_at is null` → до ближайшего свипа (15 с штатно; до 30 мин на бэкоффе отсрочек) дверь отвечает **409 `run_in_flight` на экран подписи, который платформа сама только что объявила**; соседняя кнопка Resume в том же окне отказывает ДРУГИМ словом («being settled»). Мой довод в `runs/bank.go:110-112` («its run is closed») прямо противоречит `reconcile.go:1122`; состояние строится собственным тестом дерева (`control_test.go:598-603`). Нашли НЕЗАВИСИМО 3 агента | `runs/bank.go:109` |
| Р2 | breaks | **Миграция 00026 для прогонов в полёте**: пере-снятие баз посреди прогона, уже сделавшего p_e правок, оставляет его финал на `runDonerunTotal = 2·p_e` — полностью доделанный прогон закрывается, например, 5/7 навсегда. Двух-базовая схема корректна ровно потому, что базы берутся ДО работы прогона; миграция — единственное место, берущее их ПОСЛЕ. + dispute: READ COMMITTED между двумя UPDATE допускает двойное касание строки при конкурентном финише (при штатном деплое одной реплики окно закрыто остановкой старого демона — вес мал) | `migrations/00026:20-27` |
| Р3 | breaks | **`draftWork` не моделирует порядок волн движка**: continuation над недочерновленной книгой (`draft_before ≥ chapters_before+C`, напр. прогон-предшественник упёрся в потолок на черновике) даёт `draftWork=0`, движок продолжает ЧЕРНОВУЮ волну — полоса 0/C весь прогон, stage «editing» на прогоне, который только черновит. Зеркало дефекта, который draftWork чинил | `readmodel.go:460` |
| Р4 | breaks | **`limitedBuffer` НЕ убивает чайлда** при переполнении stdout — вопреки своему комментарию (контраст: `drain()` в `engine.go:262-272` при том же переполнении явно зовёт `Process.Kill`); чайлд виснет на записи в полный пайп до чужого дедлайна (~70 с под пер-книжным мьютексом), без дедлайна — навсегда. Подтвердил чтением. + dispute: stderr — НЕограниченный `bytes.Buffer` (не-тот бинарь по сконфигурированному пути может раздуть демона до OOM; читается всё равно только первая строка) | `runner/bankapply.go:98-111` |
| Р5 | breaks | **Presence-vs-null дыра валидации**: только окна (`nullableInt`) различают явный `null` от отсутствия; `src/sense/dst/kind/id` — голые `*string`, так что `{"id":"X","src":null}` и decline с `"dst":null,"kind":null` проходят гейты, которые канонный `oneOf`/`not-required` велит отбивать 400. Подтвердил чтением (сам вводил `nullableInt` только для окон). Порчи данных нет — лишнее значение не читается; дыра контрактная | `httpapi/bank.go:48-51, 221-224, 251-259` |
| Р6 | dispute | **Раскладка на ветке SIGKILL**: `ctx.Err()`-ветка (`runs/bank.go:132`) достижима ТОЛЬКО когда SIGTERM не отработал (процесс убит по `WaitDelay`) — а это единственный путь, оставляющий полу-приземлённую пару БЕЗ отчёта, т.е. адресат `bank_corrections_incomplete`, отвечаемый общим 503; мой комментарий описывает соседнюю ветку. + `bankStopGrace` 10 с КОРОЧЕ самого длинного непрерываемого участка движка (11.3 с на документе-максимуме) — грейс истекает до фазы записи. + `runBudget` — операторская ручка свипа: понижение (к чему подталкивают комментарии) делает легальный документ-максимум навсегда неприменимым (вечный 503 вместо «split it») | `runs/bank.go:132`, `runner/bankapply.go:40` |
| Р7 | cosmetic | Утечка `bank-corrections-*.json` в StateDir при нечистой смерти демона (SIGKILL/OOM/обрыв грейса деплоя): cleanup только на defer, никто каталог не подметает; до 1 МиБ пользовательских решений на файл, бессрочно | `runs/bank.go:250` |
| Р8 | — | **Формулировка PD-400.2 (accepted-risk) требует правки**: на мульти-реплике класс 12 может быть НАСТОЯЩИМ многочасовым прогоном соседней реплики (её Start видит свой мьютекс и чистый `runs`) — слово 503 «transient holder» и мой лог тогда ЛОЖНЫ, канонно верное слово — 409 `run_in_flight`. «Ни денег, ни порчи» держится; «ни ложного слова» — НЕТ. Риск остаётся принятым (v1 = одна реплика), но акт лендинга должен описать его честно | `runs/bank.go:27-39, 182-189` |
### Находки в НАСЛЕДИИ платформы, вскрытые ревью (не код P9; полоса и дверь на них стоят)
| # | Вес | Что | Где |
|---|---|---|---|
| Н1 | breaks | **Resume НЕ-последнего прогона** (Resume пропускает любой `stopped` без проверки последнести; `lastRun` = `started_at desc`; RestartRun не трогает `started_at`): (i) полоса возобновлённого открывается на ЧУЖОЙ работе — 2/2 при нуле своих пассов (клэмп режет только >1, не двойной счёт до 1); (ii) карточка, кадр статуса и КАЖДЫЙ progress-кадр живого прогона считаются по ЧУЖОМУ финишировавшему — «ready» и замороженный бар, пока живой прогон тратит деньги. 4 агента, overlay-прогоны | `reconcile.go:1216`, `readmodel.go:487`, `runs.go:843` |
| Н2 | breaks | **`edit_wave` sticky-true × оператор убрал редактора**: прогон делает 100% купленного и закрывается ready на 50% со stage «editing»; `chaptersDone` (edit-колонка) мёрзнет → `ChaptersLeft` не падает → **шкала повторно продаёт уже переведённые главы** (симптом PD-202, записанной fixed); follow-up прогон над этой покупкой читается 0/C с первой секунды. Обе фразы комментария `sink.go:406-409` о собственном коде ложны | `sink.go:415`, `readmodel.go:388` |
| Н3 | breaks | **Флип false→true уводит `Book.chapters_done` НАЗАД** (finishedUnits через ЖИВОЙ флаг): 40/100 → 0/100 одной транзакцией при росте ревизии — клиент обязан отрисовать спад; `ChaptersLeft` раздувается обратно → предлагает купить купленное. PD-316-класс на непокрытом триггере (пин ловит только admission). Крайний случай: книга, полностью начерченная под false, НЕдочитываема редактором никаким действием API (ChaptersLeft=0 → прогон не допускается → флип недостижим) | `sink.go:415`, `readmodel.go:388`, `books.go:981-983` |
| Н4 | breaks | **Книга без материализованного дерева**: полоса (вся из `chapters`) читает 0/total весь прогон при исправно доезжающих progress-событиях; комментарий `sink.go:219` («счётчики придут из progress-события») ложен уже в HEAD. + dispute: `runs.draft_done/draft_total/edit_done/edit_total` — 2 писателя, 0 читателей (PD-314-класс, лишняя запись на каждом событии под блокировкой книги; обоснование `greatest()` на `sink.go:444-446` защищает несуществующую полосу) | `sink.go:219, 137, 447` |
| Н5 | breaks | **SSE `hello` при переподключении несёт `id = state.Position` вместо предъявленного `last`** — WHATWG-клиент фиксирует новый Last-Event-ID ДО получения догона; обрыв сразу после hello теряет кадры навсегда (включая `note`, которые контракт запрещает терять) и обезоруживает дельта-ремонт (`revision` из hello выше потерянных строк). Лечение — одна строка: при resuming слать `last`. Код рядом (`stream.go:122-126`) сам знает правильное значение | `stream.go:99` |
### Находки зоны ДВИЖКА (чужая зона — пинг оркестратору, мне не лечить)
| # | Вес | Что | Где |
|---|---|---|---|
| Д1 | breaks | **Decline не энтити-широк на обратном пути**: отклонённая сущность возвращается в карту/auto-bank/редактору через свой АЛИАС (`reverseSectionTerms` фильтрует по одному ключу, а `mined_rejects` получает только `src`), и память предъявления глушит стоп об этом НАВСЕГДА — нарушен собственный контракт «a declined term never re-enters» (`miner_emit.go:59-63`); approve симметричной утечки не имеет (кластер через строку банка) | `mining.go:548`, `membank/decisions.go:598` |
| Д2 | breaks | **Неидемпотентный decline** поверхности подписанного сида, имеющей строку в дельте: первый вызов ПРИНЯТ, повтор ТОГО ЖЕ документа — 409 (гейт `decisions.go:374` судит ДО-состояние, которое свёртка сама стирает); при классе 15 предписанный ре-сенд отбивается ЦЕЛИКОМ — **обещание сходимости 503-ретрая, на котором стоит синхронная дверь, ломается**. Доказано исполнением через СОБСТВЕННЫЙ оракул репозитория (оракул 4 фаззера); фаззер структурно не достаёт (фикс-книга не пересекает сид с дельтой). Лечение: судить по ПОСТ-состоянию, как соседний `refuseInertDeclines` | `membank/decisions.go:374` |
| Д3 | breaks | **Потеря/порча маркера выхода после стопа** (совместная с платформой): рестарт/ретрай проходит границу банка НАСКВОЗЬ — память предъявления покрывает карту, движок «continuing» одним WARN себе в журнал — оплаченный `verify_bank` стоп исчезает молча и навсегда (память append-only). Гард `LiftBankStop` (`reconcile.go:1122-1126`) писан против движка ДО памяти v16 и этот путь не держит | `reconcile.go:424`, `mining.go:216-227` |
| Д4 | dispute | **`signature` в квитанции двери** считается от карты, которую переписывает ЛЮБАЯ граница майнинга (запись карты на `mining.go:191` — ВЫШЕ решения о стопе): `signature != null` не означает состоявшегося стопа; `surfaces`/`undecided` дрейфуют между двумя вызовами владельца; `undecided: 0` достижим при непредъявленных решениях (кап top-200 вытесняет). У карты ЕСТЬ идентификатор (`signaturemap.go:25-29`) — шов его не читает | `pipeline/bankdecisions.go:549-598` |
| Д5 | cosmetic | `spawn.go:159-165` (платформа, но про движок) цитирует упразднённый D39.144-контракт стопа («halts whenever undecided terms remain») — при памяти v16 фактически ложно и питает дырявый гард Д3 | `spawn.go:159-165` |
### Ось «стоимость» — мои живые замеры + вердикт агента (сошлись)
- **Строка 186 единого бэклога ПРОТУХЛА — рекомендация оркестратору: закрыть.** Механизм построен
и применён: агент прогрепал ВСЕ call sites `writeJSON` (коллекции, карточка, capabilities, POST
двери; SSE корректно НЕ сжимается). Мои замеры на стенде tmp9stand (команды и заголовки — сырьём):
банк 437 Б / 7.2 мс холодным, повтор с `If-None-Match`**304 / 0 Б / 2.6 мс**; юниты главы
3008 Б → **740 Б gzip (×4.1)**; gzip ниже порога 1024 Б честно не применяется; resume SSE за
головой → 204 / 1.3 мс. Взамен предлагаю ДВЕ новые стоимостные строки (ниже).
- **Карточка книги — 5 коррелированных сканов `chapters` на один GET** (3 в runRow: draftChapters
×2 + editChapters, +2 в bookColumns: chaptersDone + noteCount), и те же 2 — на КАЖДУЮ строку
страницы библиотеки (×100). Эмпирика агента на живом PG 18.4, фикстура в форме миграции 00002,
книга 2283 главы: как написано — 1.359 мс; те же числа одним LATERAL `count(*) filter(...)`
0.307 мс (×4.4). Мой контрольный EXPLAIN на реальной схеме tmp9stand: **6 SubPlan-ов в плане,
5 исполняются** (ELSE-ветка never executed) — форма подтверждена. PG повторные текстовые
вхождения подзапроса НЕ дедуплицирует (доказано side-effect-последовательностью), CASE-ветки
честно short-circuit.
- **`emitProgress` повторяет 3-скан агрегат по ВСЕЙ книге на каждое progress-событие** (движок шлёт
его на каждый разрешённый юнит каждой волны) — ПОД блокировкой строки книги: O(глав × юнитов)
вместо O(юнитов), на большой книге — секунды суммарного удержания блокировки за прогон.
- cosmetic: `MkdirAll(StateDir)` на каждый вызов двери — место одному разу в конструкторе Service.
### Чистые оси (проверено — не опровергнуто)
Линза `bugs:service-and-seams`**0 находок** (проверены: cleanup temp-файла по всем выходам;
редакция refusals — единственная ветка с путём; вердикт-таблица против всей полосы exit.go; ключи
только на translate; refcount lockBook; SpendBaseline из колонок ПОПЫТКИ; согласованность
bankCountsTx; проекции без утечек словаря/путей; build/vet/тесты). Сквозные not_refuted (по многу
агентов): взаимоисключение «дверь × спавн» в заявленную сторону ДЕРЖИТСЯ на одной реплике (все 5
путей спавна упираются в `finished_at is null` = предикат HasLiveRun); правило одного писателя двух
файлов решений; сходимость повтора того же/другого документа при ЦЕЛОМ гейте Д2; SIGTERM после
записи не теряет квитанцию (`Exited=true` глотает ctx.Err — совпадает с диском); идентичность
термов через пересборку банка (id из ключа уникальности); сериализация чеканки кадров и штамповка
ревизий чисты; чтения на одном снимке; идемпотентный ключ Start не клинит за очередью мьютекса.
### Финальная батарея по дереву (после всех приёмочных правок; дерево ревью НЕ меняло)
`go test ./... -race -count=1 -v` с тремя гейтами → **EXIT=0, 18 пакетов ok, SKIP=0,
`^=== RUN` = 781**; DATA RACE — нет. (На сдаче было 775 — рост на приёмочных доборах, число
командой.)
### Вопросы/предложения оркестратору
1. Диспозиция Р1Р8: что чиню в этом дереве до лендинга, что строками регистра. Готов завести
пакет строк PD-40x по всем группам после твоего вердикта (не завожу до слова — рядом лендинг).
2. PD-400.2: скорректировать формулировку accepted-risk по Р8 (снять «ни ложного слова», описать
мульти-репличный суб-кейс) ДО акта лендинга.
3. Строку 186 закрыть (замер выше), взамен — две стоимостные строки (5-скан карточка ×100 на
библиотеку; emitProgress O(глав×юнитов) под блокировкой) куда решишь.
4. Д1Д5 — пинг бэкенду твоим каналом; Д2 ломает обещание, которое МОЯ дверь даёт на проводе
(сходимость ретрая 503) — до его лечения в движке слова канона §applyBankCorrections о ретрае
верны не для всех документов.
5. Н1Н5 — наследие: Н1 (resume не-последнего) и Н5 (hello id, лечение в одну строку) выглядят
дешёвыми и болезненными; Н2/Н3 упираются в продуктовое решение о смене формы конвейера на
живой книге.
## ПАК P9 ОТРАБОТАН — дверь правок банка смонтирована, ключи едут, полоса сквозная; цепь живого прогона ПРОБИТА живьём (сессия платформы, 27.08)
Дерево передаётся на лендинг. Опись: `git status --short -- platform/` → 31 изменённый + 9 новых
файлов, все в зоне; вне `platform/` не тронуто ничего.
### Таблица комплектности против §3 (пункт → сделано → каким ИСПОЛНЕНИЕМ подтверждено)
| §3 | Что сделано | Исполнение |
|---|---|---|
| §3.1 дверь | `POST /v0/books/{bookId}/bank/corrections` в `contractSurface` (монтаж по `Deps.Bank`, кап тела = канонный 1 МиБ = `DefaultMaxBody`); строгий декод (`DisallowUnknownFields` + запрет хвостовых байт), вся канонная валидация формы с JSON Pointer'ами; `Capabilities.bank_corrections_enabled` = факт монтажа; словарь шва `ingest/bankdecisions.go` (запрос v1 / отчёт v2, аллоулист); канал `runner.BankApply` (прямой чайлд, SIGTERM-грейс, потолок чтения); вердикт `runs.bankVerdict`; квитанция-проекция с переводом `edit_wave→refinement` и отказом на неизвестное слово; `refusals[]` в конверте `Problem` + коды `bank_corrections_refused`/`bank_corrections_incomplete` | живой пробой — `docs/p9/door-live-probe.md` (превью → правка → ретрай `already_applied` → отказ 409 с указателем → resume → 409 `run_in_flight` при живом прогоне, тела дословно); юнит-пины `internal/httpapi/bank_test.go` (7 на сдаче; 9 после приёмочных доборов — превью и обрыв на потолке), `internal/runs/bank_test.go` (6), `internal/ingest/bankdecisions_test.go` (2); посадки M1, M2 |
| §3.1 раскладка кодов | заказанное: 14→409 `bank_corrections_refused`+`refusals[]` · 15→503 `bank_corrections_incomplete` · 12→409 `run_in_flight` · тело>1МиБ→413 · >5000 и форма→400. Моя половина с доводом: **13→503 `service_unavailable`** (не мигрирован — оператор, транзиентно; различим от 15 по коду) · **19 и незнакомые члены полосы→503 `service_unavailable`** (рассинхрон сборок; какое из двух других ремеди — неизвестно по построению) · **10/11→500** (оба входа глагола рендерит платформа) · **exit 5 и таймаут бюджета→503 `service_unavailable`** (рестарт деплоя; SIGTERM-контракт глагола graceful). Три ремеди («пере-реши»/«повтори то же»/«позови оператора») не сливаются | пин-таблица `TestBankVerdictKeepsTheRemediesApart` + `TestTheDoorsFailuresKeepTheirRemediesApart`; посадка M1 (слияние 15 в 503-generic) поймана; опровергатель кодов: «(а) слияние ремедий — не опровергнуто по всем девяти строкам» |
| §3.2 синхронность | вызов синхронный, бюджет = `runBudget()` (60 с — класс вызовов движка); пер-книжный мьютекс `lockBook` в `runs.Service`, его берут corrections И `Start` И `Resume` (Start — та же гонка спавна, что resume); проверка живого прогона — ПОД мьютексом; гейт готовности книги (`readyToTranslate`) — как у Start (находка опровергателя) | `TestAResumeWaitsOutALiveCorrectionCall` (resume ЖДЁТ живой вызов двери, канал-гейтед фейк); `TestCorrectionsRefuseWhileTheBookIsBeingTranslated`; живьём — шаг 11 пробоя (409 `run_in_flight` на живом прогоне); синхронность ДЕРЖИТСЯ: живой вызов двери на стенде — доли секунды, потолок глагола 5000 подобран движком под таймаут вызывающего |
| §3.3 ключи | `TM_PLATFORM_ENGINE_KEYS_PATH` (абсолютный или отказ на буте; ⚠ суффикс `_PATH`, не `_FILE``*_FILE` в зоне значит «файл со значением секрета», гейт `TestEverySettingThisServiceReadsIsPrinted` это и поймал) → `runner.TranslateArgs` кладёт `--keys-file` ТОЛЬКО на `translate`; в окружение юнита ключи не кладутся; пусто = WARN на буте | `TestTheDeploymentKeyFileReachesTranslate`, `TestTheSpawnedUnitCarriesTheDeploymentKeyFile` (argv юнита + отсутствие ключей в Env); живьём: движок с несуществующим файлом падает громким «--keys-file … cannot be read», с файлом — пре-флайт пройден (лог пробоя); посадка M3 поймана |
| §3.4 полоса | ОДНА монотонная доля через обе волны: `done = draftBar + lastBar`, `total = draftWork + ceiling` (редактор) / `ceiling` (без), где `draftWork = clamp(chapters_before + ceiling draft_before)` — знаменатель считает работу ЭТОГО прогона (правка по находке опровергателя: continuation поверх начернённого задела кончал ready на 50%); две базы в `StartRun`, пере-базирование при снятии стопа УДАЛЕНО; подпись `progress.stage` (`drafting`/`editing`, открытый словарь); кадр `progress` и старт-квитанция несут то же; миграция `00026` (live-прогоны — обе базы пере-сняты верными предикатами, законченные — аппроксимация, названо в самой миграции) | живьём: 0/6 drafting → стоп 3/6 editing → resume 3/6 (БЕЗ обнуления) → ready 6/6 (лог пробоя); пины `TestTheBarIsOneMonotonicFractionThroughTheSigningStop`, `TestARunOverADraftedBacklogOwesOnlyTheLastPass`, `TestADraftOnlyDeploymentCountsItsOneWaveOnce`, `TestASecondRunsBarStartsAtZeroOverAHalfFinishedBook`, `TestTheRunsBarNeverExceedsWhatItBought`; посадки M4, M5 пойманы |
| §3.5 PD-399 | `pending_decisions`/`complete` сняты со всех трёх носителей (`BankCounts`+кадр `EventBank`, `wireBankPage`, подзапрос к мёртвой `bank_decisions` ушёл); пин ПЕРЕПИСАН на отсутствие | `TestTheBankAggregatesRideOnTheFirstPageOnly` пинит ОТСУТСТВИЕ; живьём: `GET /bank` на стопе и после прогона — полей нет (лог пробоя, шаги 5 и 13); строка PD-399 → fixed |
| §3.6 конвенция пути | `runner.projectDB()` и парс `book.yaml` УДАЛЕНЫ; путь банк-экспорта берётся из конверта `artifacts.bank_export`, который движок публикует в `manifest --json` (движковая половина — d1eb8a9); `refreshBank` кормится манифестом той же refresh-пачки; движок без конверта = громкий отказ, долг ретраится | `TestTheBankIsReadAtThePathTheEnginePublished`, `TestAnEngineWithoutTheEnvelopeIsAFailureRatherThanAnEmptyBank`; живьём: банк пробной книги материализовался по опубликованному пути (шаг 13) |
### Числа сдачи (каждое — командой)
- Батарея с ТРЕМЯ гейтами (`TM_PLATFORM_TEST_DSN` · `_ENGINE_BIN`+`_BOOK_TEMPLATE` · живой
пользовательский systemd): `go test ./... -race -count=1 -v` → **EXIT=0, 18 пакетов ok,
`grep -c -- '--- SKIP'` → 0, `grep -c '^=== RUN'` → 775**; линтер `golangci-lint run`**0 issues**;
`gofmt -l` пусто, `go vet ./...` чисто. Лог — `~/tm-p9-work/final-battery.log` (вне репо).
- Тест-функции зоны: `grep -rh '^func Test' platform --include='*_test.go' | wc -l`**561 (HEAD) → 580**.
- Регистр: `python3 docs/scripts/counts.py --check`**401 строка, открытых 97** (на сдаче 3/27/67;
после пере-взвеса PD-401 приёмкой — 3/28/66); было 398/96 — PD-399 закрыта, PD-400/PD-401 заведены;
«битая форма: []», хвост чист. ⚠ Первая редакция этой строки называла «400 строк» — снято
пере-счётом ревьюера, число выше — командой.
- Миграции: `00026` добавлена, отпечаток в `migrations.sha256`; `pgstore.Migrate` гонялся каждой
тестовой базой батареи (сотни накатов за прогон).
- **Посадки мутаций — 5, пойманы 5/5, в копии с каноном** (`cp -a --parents platform
docs/architecture/14-api-contract`, скрипт `~/tm-p9-mut/run-mutations.sh`, логи `~/tm-p9-mut/*.log`),
вердикт по ДЕЛЬТЕ против чистой базы ТОЙ ЖЕ копии (база EXIT=0, FAILS пусто) и по ТОПИЧНОСТИ:
| # | механизм | что посажено | что упало (ровно топичный пин) |
|---|---|---|---|
| M1 | дверь | класс 15 слит в `ErrBankUnavailable` | `TestBankVerdictKeepsTheRemediesApart` |
| M2 | провод | кортеж перестал требовать `sense` | `TestACorrectionRequestIsValidatedWholeWithPointers` |
| M3 | ключи | `Cfg.KeysFile` не доезжает до argv | `TestTheSpawnedUnitCarriesTheDeploymentKeyFile` |
| M4 | полоса | знаменатель снова `2×ceiling` | `TestARunOverADraftedBacklogOwesOnlyTheLastPass` |
| M5 | полоса | возвращено пере-базирование на снятии стопа | `TestADraftOnlyDeploymentCountsItsOneWaveOnce` |
### Живой пробой (§4.2) — цепь срослась, и за $0
Полный лог — **`docs/p9/door-live-probe.md`**. Суть: книга через живой интейк → прогон до банкового
стопа → превью → правка → ретрай (`already_applied`) → отказ 409 с указателем → resume → 409
`run_in_flight` при живом прогоне → ready → **банк следующей границы несёт правку** (方源 →
approved «Фан Юань-П9», задеклайненная поверхность исчезла из предложений). ⚠ Провайдер — локальная
$0-заглушка (`local`-модель из `models.yaml`), потому что **в файле ключей деплоя нет ни одного
живого провайдерского ключа** — движковый пре-флайт называл недостающие ключи по имени для всех
шести облачных провайдеров (значения ключей в сессию не читались, гардрейл `.env` цел; замер — отказ
резервации при потолке $0.000001, ДО вызова провайдера). Ось «деньги»: леджер стенда сверен ДВУМЯ
путями на нетривиальном состоянии (3 холда/3 возврата/3 расчёта) — CLI `balance` и сырой SQL сошлись
до цента ($25.000000).
### Опровергатели (§4, заказ) — 2 агента, обе панели принесли «ломает», всё абсорбировано
1. **Раскладка кодов.** «Ломает»: у двери не было гейта готовности книги (не-готовая книга доезжала
до движка и возвращалась 500-ложью «наш дефект») — **починено** (`readyToTranslate` под мьютексом
→ 409 `book_not_ready`). «Спорно»: транзиентные держатели флока вне сериализации (границная
материализация, ручной tmctl) читаются словом `run_in_flight`; мьютекс внутрипроцессный
(мульти-реплика теряет сериализацию) — **заведено PD-400**, лечение вне заказа. Утечка пути
темп-файла в `refusals[].detail` на движковых капах — **починено** (редакция пути в
`ApplyBankCorrections`). Косметика: SIGINT вместо SIGTERM — починено (`syscall.SIGTERM`); пустой
`rejected` при exit 14 — гард добавлен; хвостовые байты за JSON — отвергаются (`dec.More()`).
ПРИНЯТО БЕЗ ПРАВКИ с причиной: `since_chapter: 3.0` (валидный integer по JSON Schema) реализация
400-ит — генерённые клиенты целых через точку не шлют, названная узость; item-код `missing` на
присутствующем-но-пустом члене — словарь item-кодов открыт.
2. **Форма полосы.** «Ломает» №1: continuation поверх начернённого задела — ready на 50% с вечным
`drafting` — **починено** (`draftWork` в знаменателе и в stage; пин + посадка M4). «Ломает» №2:
бэкфил замораживал полосу легаси verify-прогонов — **починено** (миграция пере-снимает обе базы
live-прогонов верными предикатами; для ЗАКОНЧЕННЫХ легаси — названная аппроксимация в тексте
миграции). «Спорно»: флип `edit_wave` при живом прогоне двигает знаменатель — **заведено PD-401**
(окно секунды, лечение трогает словарь шва). ПРИНЯТО С ПРИЧИНОЙ: пере-нарезка внутри прогона
обнуляет полосу (снос resolutions — правда о пере-резанной книге, осознанное исключение);
`stage='editing'` на банковом стопе (статус `awaiting_bank` на экране первичен); старт-квитанция
читает «последний прогон книги» (вставка видна в своей транзакции, гонка требует регресса часов).
### Диспозиции по норме §3 п.8 (греп открытых строк по ПОЛНЫМ путям моих файлов — 46 совпадений)
- **PD-399 — ЗАКРЫТА** этим паком (см. §3.5, пин назван в строке).
- **PD-370 — предлагаю ЗАКРЫТЬ приёмке**: зонная половина закрыта 22.08, контрактная — минором
0.5.0 (D39.161: ноль вхождений отменённой модели в каноне), ратифицированная замена (дверь правок)
построена этим паком. Закрытие — акт лендинга, не зоны: лекарство контрактной половины не в моём дереве.
- **PD-281 — остаётся open, дописка внесена**: сквозная форма сменила знаменатель, но прогон над
книгой, полной в обоих проходах, по-прежнему невидим до ready; канонному минору полосы НЕ
наследовать «the fraction always reaches one» без оговорки.
- **PD-396 — остаётся** (вопрос владельца по строке); замечено: счёт нерешённости теперь едет
квитанцией двери (`signature`), `UnsignedBankTerms` так и мёртв — снятие поля не брал (не в §3).
- Остальные 42 совпадения (PD-6…PD-397 по списку грепа) — **оставлены: совпадение по файлу, не по
механизму** — пак их механизмов не трогал; полный список воспроизводится:
`python3` -скриптом по `DEFECT_REGISTER.md` (колонка «Где» × список файлов описи).
### Чего в паке НЕТ (по §3.7 — пропуски подписаны)
Читающая сторона банка (221/224/226 — на стопе провод банка ПУСТ, видно живьём в пробое, шаг 5) ·
sqlc · строка 198 · PD-375…PD-398 кроме PD-399 · воркер решений (синхронность ДЕРЖИТСЯ — замер, не
рассуждение: живой вызов двери — доли секунды при потолке, подобранном движком под таймаут) ·
снятие обхода `--verify-bank` из пинга №21 (не в §3; `bank_released` остался в спавне и чтениях глав).
### Obstacle — что НЕ удалось и что НЕ проверено
- **Живой пробой на ОБЛАЧНОМ провайдере не удался: в файле ключей деплоя нет ни одного живого
ключа** (deepseek/zai/kimi/gemini/mistral/openai — все названы движком отсутствующими; grok — упёрся
в конфиг аддитивного биллинга раньше ключей). Цепь пробита на `local`-заглушке — она доказывает ШОВ
(дверь → глагол → файлы → resume → пере-сбор банка), но НЕ качество и НЕ поведение под латентностью
настоящего провайдера. Строка 202 «книга насквозь по-настоящему» упирается теперь ровно в ключи.
- **Таймаут-ветка двери (SIGTERM по бюджету) и класс 15 живьём не воспроизводились** — юнит-пины
есть, живого файлового отказа не строил.
- **`bank_corrections_enabled: false ⇒ 404` живьём не гонял** (нужен второй демон без движка) —
юнит-пин `TestAnUnmountedCorrectionDoorAnswers404AndSaysSoInCapabilities`.
- **Стенд-эффект на батарею**: живой юнит стенда оставил `tm-runs.slice` без контроллеров памяти —
`TestARunIsBoundedByItsOwnCgroup` падал, пока слайс не сброшен (`systemctl --user stop
tm-runs.slice`); это среда, не регрессия — на чистом слайсе зелёный. Приёмке знать при пере-прогоне.
- **Пробные артефакты вне репо**: `~/tm-p9-work/` (стенд, логи батарей, fake-провайдер),
`~/tm-p9-mut/` (копия с каноном + логи посадок). БД стенда `tmp9stand` на локальном PG 5432 — можно
сносить. В КАТАЛОГЕ КНИГ стенда остались мои крафтовые книги — репо не касаются.
- Сырые логи двух опровергателей не сохранены в зону — их отчёты абсорбированы сюда и в PD-400/401.
### Вопросы оркестратору
1. **Канонный минор полосы** (D39.160: полоса едет паком, механика версии — за тобой): деплой этой
сдачи шлёт `progress.done/total` в сквозной семантике (chapter-passes работы ПРОГОНА) + новый член
`progress.stage` (открытый словарь `drafting`/`editing`) — впереди объявленного 0.5.0 по прецеденту
`Run.stop_requested` (`v0.go`, ⚠-коммент у `wireProgress`). Канону нужны: пере-описание `Progress`
(снять «restarts from zero», НЕ наследовать «always reaches one» — PD-281), член `stage`. Кадр
`progress` меняется тем же минором (`EventProgress` ссылается на ту же схему).
2. **PD-370** — закрытие строкой за лендингом (см. диспозиции).
3. **Провайдерские ключи деплоя** — нужен хотя бы один живой ключ, чтобы строка 202 прошла на
настоящем провайдере; какие имена ключей движок ждёт — в логе пробоя.
### Аддендум приёмки (27.08, вечер) — блокер ревьюера по полосе: причина починена, лендить ли — слово владельца
Ревьюер приёмки (`textmachine-29`) принёс блокер: прогон, стартовавший при `edit_wave = false` с
переворотом флага ПОСЛЕ старта (движок объявляет форму волн первым progress-событием), делал всю
купленную работу с полосой `0/N` навсегда — база снята флаг-зависимым предикатом момента старта и
не пере-базируется. Мой пак знал механизм (текст миграции 00026 его называл) и вылечил только
legacy-прогоны на буте, оставив генератор живым; строка `PD-401` его называла, но с заниженным
весом и формулировкой «окно секунды».
**Сделано по первому заказу оркестратора (до его поправки «жди слова» — работа уже была зелёной,
откат по слову, не молча):** причина, не следствие — обе базы снимаются на ФИКСИРОВАННЫХ колонках
(`chapters_before` — редакторская, `draft_before` — черновая, `StartRun` без CASE по флагу), пара
«числитель+база» выбирается ЖИВЫМ флагом в момент чтения (`runDone`: `draftBar+editBar` против
`draftOnlyBar`); миграция 00026 переписана (live-прогоны — обе базы на фиксированных колонках);
пин ровно на прод-порядок переворота — `TestAFlagThatFlipsAfterStartDoesNotStrandTheBar` (флаг
false ДО `StartRun`, переворот ПОСЛЕ, вся работа → 2/2). Сценарий блокера сходится: до переворота
0/C, после — `draftWork = 0` ⇒ total = C, редактура двигает 0→C, ready C/C.
**Побочно вскрыто и починено (класс PD-1):** два старых пина `recut_test.go` пережили снос
сегментной модели с ложными словами — `TestLiftingTheSigningStopRestartsTheBarFromZero` объявлял
«Mutation caught: dropping the re-capture from the re-open», а ре-кэпчер снесён и тест зелёный;
переименован в `TestLiftingTheSigningStopLeavesTheBarWhereItStood` с честным свойством и живым
mutation-catch (спутать пары «числитель×база»); комментарий
`TestTheBarAndItsBaselineCountTheSamePass` переписан под пары-по-колонкам.
**PD-401 пере-формулирована и пере-взвешена** (info → minor, переезд в minor-секцию) по слову
оркестратора: постоянная слепота платной работы, не транзиентный скачок; лечение в дереве названо
в строке, статус open до решения владельца о составе лендинга. Регистр: 401 строка, 97 открытых
(3/28/66), оба гейта чисты; полный `pgstore` зелёный (`go test ./internal/pgstore/ -count=1` → ok).
Ось денег на нетривиальном состоянии (открытый холд + после расчёта) ревьюер исполнил на этом
дереве сам — расхождений нет.
### Аддендум 2 (28.08, по слову владельца «техдолг в паке не держим») — PD-400 разобрана по половинам
**Половина 1 (слово `run_in_flight` шире правды) — ЗАКРЫТА в дереве**, и лечение оказалось точнее,
чем строка думала: под мьютексом и ПОСЛЕ проверки строки прогона класс 12 от глагола прогоном быть
не может по построению (Start/Resume ждут тот же мьютекс, реконсилер рестартует только живые строки,
которые проверка видит) — значит `run_in_flight` отвечается ТОЛЬКО из проверки собственной строки, а
класс 12 глагола едет `503 service_unavailable` «занято, повтори позже» с ERROR-строкой оператору.
Пин — обновлённый кейс вердикт-таблицы; `-race` по четырём задетым пакетам зелёный, линтер 0 issues.
**Половина 2 (внутрипроцессный мьютекс) — граница v1, названная с условием и ценой** в строке и в
шапке `bank.go`: сериализация сужается до пер-репличной при второй реплике; цена — холостая попытка
и «повтори позже», не ложь и не деньги; лечение при второй реплике — арбитр в хранилище. Предложен
перевод половины в «Принятый риск» словом лендинга. **`supervisor.go:35` проверен по существу —
ЧЕСТЕН для своего дев-пути** (ключи там законно едут окружением/наследованием); дописана одна
страховочная фраза «прод передаёт ключи аргументом `--keys-file`; копировать этот канал в прод-спавн
— ловушка паритета, которую флаг и закрыл». Сдвинутые этой правкой якоря runs.go пере-нацелены,
оба гейта регистра чисты.
## ПАК P9 — ЗАПИСКА-ПЛАН (сессия платформы, 27.08, промт `docs/PLATFORM_P9_SESSION_PROMPT.md`)
План до правок, по §6 промта. Итоги и таблица комплектности — записью сдачи ниже по завершении.
1. **§3.3 Ключи движку.** Новый конфиг `TM_PLATFORM_ENGINE_KEYS_FILE` (абсолютный путь или отказ на
буте, как `StateDir`) → `RunnerConfig.KeysFile` → `runs.Config` → `runner.TranslateArgs` получает
`--keys-file` (флаг принимает ТОЛЬКО `translate` — `cmd/tmctl/invocation.go:151-157`). В окружение
юнита ключи не кладутся. Пусто = флаг не передаётся (сегодняшнее поведение), с WARN на буте.
2. **§3.4 Полоса.** Форма: `done/total` пере-определяются как «проходы-главы через ОБЕ волны»:
`total = 2×ceiling` при редакторе (`edit_wave`), иначе `ceiling`; `done = clamp(главы-с-черновиком
draft_before) + clamp(главы-с-последним-проходом chapters_before)`, каждый clamp в
`[0, ceiling]`. Монотонно по построению (счётчики юнитов только растут, базы фиксированы на
старте), база и кап прогона сохранены (комментарий `readmodel.go:437-445` чтится). Миграция 00026:
`runs.draft_before` (бэкфил = `chapters_before`); `StartRun` снимает ОБЕ базы, ветвление по
`$3=verify_bank` уходит. Подпись «что делается сейчас» — новое поле `progress.stage`
(`drafting`/`editing`, открытый словарь, клиент рисует фразу сам) в JSON и в кадре `progress`.
⚠ Канон 0.5.0 описывает `Progress` посегментно — канонный минор к смене едет с лендингом
(D39.160: полоса ЗДЕСЬ, механика гейта версий — у оркестратора); прецедент поля впереди
объявленной версии — `Run.stop_requested` (`v0.go:138`).
3. **§3.5 PD-399.** Снять `pending_decisions`/`complete` с `BankPage` и `EventBank`: `BankCounts`,
`payload()`, `wireBankPage`, `listBank`; пин `reading_test.go:113` ПЕРЕПИСЫВАЕТСЯ на новый
контракт (присутствие total/signed + ОТСУТСТВИЕ снятых полей) — правка с пином по заказу.
4. **§3.6 Дубль конвенции.** Движок публикует конверт `artifacts` (`bank_export` и др.) в
`status --json` И `manifest --json` (`backend/internal/pipeline/status.go:110-134`, лендинг
d1eb8a9). `ingest.Manifest` получает конверт; `refreshBank` берёт путь из ТОЛЬКО ЧТО прочитанного
манифеста той же refresh-пачки; `runner.projectDB()` и парс `book.yaml` удаляются. Без фолбэка:
старый движок без конверта = ошибка с именем причины, долг ретраится (порядок деплоя «движок
первым» — норма зоны).
5. **§3.1+§3.2 Дверь.** Маршрут `POST /books/{bookId}/bank/corrections` в таблице `contractSurface`,
монтирование по `Deps.Bank != nil`; `Capabilities.bank_corrections_enabled` = тот же факт; false ⇒
404. Вызов СИНХРОННЫЙ: обработчик валидирует проводную форму (строгий декод, оба потолка ДО
спавна: >1 МиБ → 413 через кап тела маршрута, >5000 и вся форма → 400), рендерит документ движка
(`tm-bank-decisions-v1`, null-окна → 0), кладёт во временный файл под `StateDir`, зовёт
`tmctl bank-apply` прямым чайлдом с бюджетом `RunBudget` (60 с — класс бюджета вызовов движка),
декодирует отчёт v2 аллоулистом, отвечает квитанцией. Сериализация: пер-книжный мьютекс в
`runs.Service`, его берут corrections И `Resume`/`Start` (та же гонка на спавне свежего прогона);
проверка «жив прогон» — ПОД мьютексом (`ReadBookForRun.HasLiveRun` → 409 `run_in_flight`).
Раскладка кодов: заказанное — 14→409 `bank_corrections_refused` (+`refusals[]` в `Problem`),
15→503 `bank_corrections_incomplete`, 12→409 `run_in_flight`, тело >1 МиБ→413. Моя половина, с
доводом в отчёте: 13 (схема не мигрирована — оператор, транзиентно) → 503 `service_unavailable`;
19 (класс без номера — рассинхрон сборок) → 503 `service_unavailable`; 10/11 (сломан вызывающий/
деплой) → 500 `internal_error`; exit 5 и таймаут бюджета → 503 `service_unavailable`. Ни один не
сливается в неразличимый 500: три ремеди («пере-реши»/«повтори то же»/«позови оператора»)
различимы по коду. `depth: edit_wave→refinement`; неизвестный depth движка НЕ форвардится
(утечка имени волны) — 500 с ERROR-строкой.
6. **Самопроверка §4:** батарея с тремя гейтами на копии-с-каноном (скипы отдельным `-v`-грепом);
живой пробой двери на стенде против настоящего движка (книга → банковый стоп → preview → правка →
resume → следующий прогон видит правку; лог в отчёт); посадки мутаций — минимум по одной на
дверь/ключи/прогресс, вердикт по дельте и топичности; опровергатели (2 агента): раскладка кодов и
форма полосы.
7. **Не делаю:** читающая сторона банка (221/224/226) · sqlc · строка 198 · PD-375…PD-398 кроме
PD-399 · воркер решений · снятие обхода `--verify-bank` из пинга №21 (не в составе §3; трогаю
`bank_released` только в чтении полосы).
## ПАК P8-REVIEW ПРИНЯТ И ЗАЛЕНДЁН (оркестратор №19, 27.08) — ратификация D39.159
Пак принят целиком: четыре оси, 24 новые строки, 17 дописок, каталог воспроизведения. Тело приёмки —
нота **D39.159**, здесь только то, чего в ноте нет, и что зоне нужно знать для следующего пака.
**Что приёмка пере-мерила своим исполнением, а не приняла на слово.**
· Батарея пере-прогнана с ТРЕМЯ гейтами на своей базе — 18 пакетов, EXIT=0, линтер 0 issues,
скипов 0 (счёт снят отдельным `-v`). Ваши четыре числа воспроизвелись.
· ⚠ **Движок для гейта живого рендера собран из HEAD, то есть уже с лендингом бэкенда `d1eb8a9`,**
которого 24.08 ещё не было. Это сильнее вашего замера: шов пережил переписанную входную дверь.
· `PD-379` подтверждён чтением: `pump` получает разрешённого пользователя и до конца соединения
строку сессии не смотрит; маршрут — `internal/httpapi/v0.go:84`. Ваш артефакт честен вплоть до
оговорки про 404 у `logout-all` на дев-профиле.
· `PD-376` подтверждена СВОЕЙ посадкой: `min` → `max` в `internal/pgstore/runs.go:588`, копия с
каноном по вашему же рецепту, чистая базовая линия EXIT=0/FAIL=0 — после мутации батарея
ОСТАЛАСЬ зелёной, названный пин не упал. Спор по `PD-159` обоснован.
**Диспозиции пяти ваших вопросов.**
1. **Статусы `PD-159`, `PD-293`, `PD-361` НЕ пере-открываю.** Описанный строкой дефект из кода ушёл —
недостаёт не лекарства, а ПИНА, и пробел уже несёт своя открытая строка. Пере-открытие сказало бы
«денежный баг вернулся», что ложно, и посчитало бы один пробел дважды. **Токен `ОСПОРЕНО(PD-N)`
ратифицирован** как стоячий инструмент — с условием двусторонней ссылки, которое вы уже выполнили
(проверил все три пары). Инструмент хороший: он держит спор грепаемым вместо прозы.
2. **Обе правки ратифицированы** — норма §3 п.3 и эррата §13. Отдельно одобряю дисциплину эрраты:
абзацы политики оставлены до закрытия `PD-379`. Переписывать обоснование раньше, чем закрыт
дефект, значило бы задним числом объявить нормой то, что дефектом и признано.
3. **Галочку `ASVS 7.4.1` пометил сам** — эрратой в БАННЕРЕ архива, а не правкой строки. ⚠ Файл
`docs/archive/platform-PROGRESS-P0-P3.md` — В ВАШЕЙ зоне, не в чужой; чужая для вас — `docs/`
корня. При архиве правку делает лендер, поэтому сделал я, но перестраховка стоила вам вопроса.
4. **Норма принята и вписана — `ENGINEERING_STANDARDS` §3 п.8.** Машинная половина заведена строкой
бэклога 225 в `docs/PROGRESS.md`. ⚠ **Ваш гейт замерен ПРЕЖДЕ постройки и требует ПОЛНЫХ путей:**
по именам файлов он даёт 22 совпадения, почти все ложные (`main.go`, `runner.go`, `config.go` есть
в обеих зонах), по полным путям — 2, и оба настоящие. Он окупился до постройки: нашёл, что якорь
`PD-157` (`backend/internal/config/book.go:250`) убит МОИМ лендингом `d1eb8a9` — требование уехало
на `:321`. Пере-нацелил и назвал причину. Симметричное правило дописано в норму: якорь, убитый
переездом, чинит тот, чей переезд его убил.
5. **`PD-398` — ОТКЛОНЁН замером, строка пере-формулирована.** Предложенное `n != shape` краснит СЕМЬ
законных строк: колонки в `counts.py` читаются С КОНЦА НАМЕРЕННО, и комментарий на `:132` приводит
ровно этот случай. Числа регистра из-за избытка не врут — `PD-99` и `PD-197` разбираются верно.
Настоящий остаточный риск другой: избыток в ХВОСТОВОЙ ячейке. ⚠ Я воспроизвёл его на себе, пока
правил эту же строку: вписал вертикальную черту в ячейку статуса, и `open` мгновенно стал «иное»,
а счёт открытых — 95 вместо 96. Экранирование `\|` парсеру НЕ помогает, он режет по сырому символу.
**Что осталось вам и чего я НЕ делал.** Строки пака не чинил ни одной — это работа кодового пака.
Ваших «чего не проверял» я тоже не закрывал: сырые логи 42 агентских посадок, `-race` у выживших,
«до 17 раз» в `PD-377`, живая проба `PD-96`, экономика `PD-215`, кардинальность лейблов. Они
остаются названными пробелами, а не тихими.
**Об ошибках, которые вы назвали сами** (пять, от овер-атрибуции `PLATFORM_DIRECTION` до неполной
описи): называть их — правильно, и опись, недобравшая три файла из пяти, действительно стоила бы
лендеру правки НОРМЫ. Продолжайте так же.
## ПИНГ №21 от оркестратора (27.08, D39.158) — обход флага банка стал лишним, но снимать его ТОЛЬКО после движка
Движок теперь сам исполняет модель D39.144: стоп банка срабатывает только на кластере, которого ни один прежний стоп не предъявлял, а возобновление БЕЗ решений продолжает прогон. Персистентная память живёт в движке (схема хранилища v16).
**Что у вас становится лишним:** снятие `--verify-bank` при возобновлении, особый случай в спавне и персистентная колонка «банк отпущен» — их работа переехала в движок, где ей место по п.1 закона шва.
⚠ **Порядок обязателен и односторонний:** сначала лендинг движка (сделан) и `tmctl migrate` по каждой книге, и только ПОТОМ снятие обхода. Обратный порядок — и каждая книга с поднятым флажком перестаёт писать авто-банк ровно в том режиме, ради которого флажок существует. Порядок деплоя «движок первым» это обеспечивает.
**Сверх того, к сведению:** заведён класс полосы `write_incomplete` = **exit 15** (и «не легло ничего», и «легла половина» — ветвление одинаковое: условие хоста, не книги; повтор безопасен). Отчёт двери — `tm-bank-decisions-report-v2`. Полоса получила два яруса и гардрейл «разрушительное действие ключуется на КЛАССЕ, никогда на принадлежности полосе» — у вас он истинен де-факто, правки не требует. Машинная таблица стопа `.bank-stop.json` СНЕСЕНА (читателя не было ни в одной зоне) — если её называют ваши доки, упоминания протухли.
**Строки бэклога, которые вас касаются:** 221 (джойн «предложено × решено × нерешено» живёт только в движке — экран подписи заставит вас пере-реализовать его закон, чего п.6 не разрешает; лечение — публикация движком, когда экран закажут) · 224 (банк-экспорт на стопе пуст).
## Текущее состояние
**⛔ ПИНГ ОРКЕСТРАТОРА №19 — 23.08, ЗОНЕ ПЛАТФОРМЫ. Три находки, все сверены моими командами; рукой в код не лезу.**
1. **Пере-нарезка книги сносит ВСЕ решения юнитов и пересчитывает прогресс из пустоты.**
`internal/pgstore/readmodel.go:127-137`=`a re-cut drops the resolutions of the previous cut` —
это не догадка, там собственный комментарий кода. Строку в ваш регистр я не заводил: решать вам,
намеренная это семантика пере-разреза или дефект, и что делать со счётчиками, которые после
этого показывают ноль сделанного на книге, где работа была.
2. **⛔ БЛОКЕР ЖИВОГО ПРОГОНА: движок на SaaS не получает провайдерских ключей** — строка **211**
единого бэклога, тело там. Ваша половина: путь к файлу ключей едет из конфига платформы в
аргументы движка, а дев-супервизор переводится на тот же механизм. Сейчас пути РАЗНЫЕ —
`internal/ingest/supervisor.go` (до-паковые строки 35-36 и 65-67 — «Provider keys reach the engine through it»; комментарий переписан лендингом P9, якорь исторический) наследует
окружение платформы, а прод-спавн ставит юниту ровно один элемент
(`internal/runs/spawn.go`, до-паковая строка 168 `Env: engineEnv(…)`; с лендингом P9 ключи едут аргументом `--keys-file`, якорь исторический). Поэтому стенд зелёный, а прод
голодает, и ни один тест упасть не мог. Пока два пути кормят движок по-разному, следующий такой
блокер снова пройдёт всю батарею.
3. **Дубль движковой конвенции у вас.** `internal/runner/artifacts.go` (до-паковые строки 65-95 — «neither project_db nor book_id»; `projectDB` снесён лендингом P9, файл ужался, якорь исторический)
сам вычисляет путь БД книги, повторяя `backend/internal/config/book.go` (до-паковые строки 163-167 — `b.ProjectDB = filepath.Join(…)`; конвенция ушла в конверт манифеста движковой половиной, якорь исторический);
смена дефолта в движке тихо уведёт ваше чтение банка на несуществующий путь. Строка **213**.
Фикс аддитивный и без ломки: движок отдаёт путь артефакта, вы выкидываете свой `projectDB()`.
⚠ Пере-именование банк-экспорта в фикс-имя — ЛОМАЮЩЕЕ, ему место в окне строки 161, не здесь.
4. **Поле, которое декодируется и не читается.** `ingest.StatusReport.UnsignedBankTerms`
(`internal/ingest/resync.go:30`=`UnsignedBankTerms`) разбирается из ответа движка и не используется
НИ ОДНОЙ строкой продакшн-кода зоны. Либо потребитель потерян при спиле пер-термного пути, либо поле
лишнее — решать вам; я называю факт, потому что мёртвое поле в структуре шва читается как контракт.
*(Промт `P8-REVIEW` жив и ждёт слова владельца. ⚠ Правка к этой же записи, 23.08: я объявил в ней ДВА
дефекта промта — реальным оказался ОДИН и он ПОЧИНЕН (миграция `00019` названа денежной, хотя это
`00019_read_model_debt.sql`, долг read-модели; ось 1 по ней ушла бы не туда). ⚠⚠ **Второй дефект РЕАЛЕН, и моё прежнее «не воспроизвёлся» было ЛОЖЬЮ — снимаю её здесь же.**
Я написал зоне «грепа `13 изменённых|6 новых|2166` в промте ноль». Греп СОВПАЛ: строка 42 промта несёт
«в дереве живёт НЕЗАКОММИЧЕННАЯ работа полигона (13 изменённых файлов и 6 новых под `eval/`)». Я обрезал
вывод грепа по ширине, увидел начало строки и принял совпадение за отсутствие. Счёт при этом действительно
протух: на 23.08 в дереве **14 изменённых и 5 неотслеженных**. Дефект ПОЧИНЕН тем же заходом — числа из промта
убраны, он теперь ссылается на единственный носитель счёта, а не держит свою копию. Записываю, потому что обещание зоне, исполненное
наполовину без объяснения, читается как невыполненное.)*
- **ПАК P8-REVIEW ОТРАБОТАН (24.08) — четыре оси прочитаны, кода не тронуто.**
**Что уезжает на лендинг (опись пере-снята командой, лендить по ней):**
`git status --short -- platform/` → `M docs/DEFECT_REGISTER.md` · `M docs/platform-PROGRESS.md` ·
`M docs/ENGINEERING_STANDARDS.md` · `M docs/STACK_DECISIONS.md` · `M docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md` ·
`?? docs/p8-review/`. ⚠ **Три последних — не находки, а правки, и две из них требуют вашего слова:**
в `ENGINEERING_STANDARDS` §3 вставлен НОВЫЙ пункт 3 (рецепт копии с каноном и правило «дельта плюс
топичность») — это правка НОРМЫ зоны; в `STACK_DECISIONS` §13 вписана эррата к обещанию «мгновенный
отзыв», которое `PD-379` опровергает; в промте пака исправлен рецепт копии. `.go` в репозитории не
тронуты (`git diff -- platform/ ':!platform/docs'` пуст), индекс чист.
**Числа сдачи (сняты ПОСЛЕ последней правки регистра):**
· `python3 docs/scripts/counts.py --check` от корня → **398 строк, открытых 96** (3 major, 27 minor,
66 info); было 374 и 72 (1/16/55). **24 новые строки** `PD-375`…`PD-398`, **17 ⚠-дописок** к чужим.
Чужие статусы и веса не сдвинуты: вся дельта открытых — мои новые строки.
· `make check` с тремя условиями → **18 пакетов, EXIT=0, линтер 0 issues**
(`docs/p8-review/battery-final.log`). Скипы лог не считает — секция «did NOT run» печатается только
при найденном `--- SKIP`, — поэтому счёт снят отдельно: `go test ./... -count=1 -v` с грепом по
`--- SKIP` → **0**. Это и есть недостающий замер к `PD-374`: на этом хосте выполнимы все три условия.
· `counts.py --lint` → по `platform/docs` один якорь, и он ЧУЖОЙ и транзитивный:
`platform-PROGRESS.md:27` целит в `backend/internal/config/book.go:163-167`, против HEAD там строка
164 — верно, — а красным его делает незакоммиченная работа соседней бэкенд-сессии, двигающая цель на
188. **Правку этого якоря я сделал и ОТКАТИЛ: не пере-нацеливать.** Прибить его к транзитному
положению значит сломать в момент лендинга или отката соседней работы.
· Посадок мутаций: мои **16** (все воспроизводимы — `plant.py` знает поимённо, логи рядом), 7 поймано,
9 выжило; агентских **42** по их таблицам (`docs/p8-review/agent-mutations.md`), 21/21. Уникальных
переживших инвариантов 15, каждый несёт строку.
**Ось → чем закрыта → находки.** Предмет каждой оси взят из промта целиком; ниже только отклонения.
1. **Деньги и леджер.** Обязательная норма исполнена дважды и на НЕтривиальном состоянии —
`docs/p8-review/reconcile-with-open-hold.txt` (8 строк леджера, ОТКРЫТЫЙ холд $0.06 в момент
замера, оба пути сошлись); независимо то же сделал агент оси на своём стенде. Вторая половина
нормы (леджер = НИЖНЯЯ граница) проверена отдельно: расход СВЕРХ холда каппится и живёт только
текстом в `note`, который не читает ни один путь кода — `internal/pgstore/credits.go:225` плюс
ноль `SELECT` по колонке. Это проектное решение, строкой не заведено.
**Находки: `PD-375` `PD-376` `PD-377` `PD-378` `PD-394` `PD-397`.**
2. **Вход, сессии, CSRF** — ось, которой не касался ни один пак. Живые пробы на демоне со СВОИМИ
короткими потолками, матрица CSRF по маршрутам, посадки, сверка с ASVS 5.0 и RFC.
**Находки: `PD-379` (единственный `vuln` пака, major) `PD-380` `PD-381` `PD-382` `PD-383`.**
3. **Очередь и джобы.** Смотрел не сам блокер `PD-169`, а его сиблингов И его лечение. Пробы на
РЕАЛЬНЫХ функциях с реальным Postgres. ⚠ Пять якорей промта по этой оси сверены и живы дословно.
**Находки: `PD-384` `PD-385` (major) `PD-386` `PD-387` `PD-388`.**
4. **Метрики — исполнением.** Снимок в покое → вызванный отказ → снимок → дифф; все три в
`docs/p8-review/axis4-metrics/`. **Ответ на вопрос оси «что оператор понял бы по этой дельте»:
почти ничего.** Переход в `stalled` по одной `/metrics` виден; отказ свипа — нет (меняется только
гистограмма длительности); гейджи при отказе чтения замирают без признака устаревания; инстанс,
у которого чтение не удалось ни разу, отдаёт нули как здоровье. Случаи, где `run abandon`
ОТКАЖЕТ, оператор различает — колонка SPENT печатает «?» ровно при пустой базовой линии, биекция;
это единственная находка пака, **опровергнутая** рефутером по всем четырём состояниям.
**Находки: `PD-389` `PD-390` `PD-391` `PD-392` `PD-393`.**
Сверх осей — из сверки регистра с деревом: `PD-396` (мёртвые поля шва) и `PD-398` (гейт формы
регистра ловит недостачу ячеек, но не избыток).
**Таблица посадок — мои, вторым рубежом поверх агентских** (`docs/p8-review/plant.py`, логи
`mutations.log` · `mutations-full.log` · `mutations-round2.log`):
| # | ось | что посажено | какой пин обязан был упасть | упал |
|---|---|---|---|---|
| M1 | деньги | снята пере-проверка «попытка уже заспавнена» в `ReleaseUnspawned` | `TestAStaleSnapshotDoesNotGiveBackTheHoldOfAnAttemptThatSpent` | ✅ |
| M2 | деньги | снят кламп по `SpendBound` в `settle` | `TestADeferredSettlementIsNotChargedForTheNextRunOfTheSameBook` | ✅ |
| M3 | деньги | `holdTx` отвечает `nil` вместо `ErrDuplicateHold` | `TestAHoldThatDebitedNothingIsRefused` | ✅ |
| M4 | деньги | снят гард `spent < 0` в `Settle` | носителя нет | ❌ → `PD-394` |
| M5 | деньги | снят кламп-в-ноль в `attemptSpend` | `TestAMeterThatWentBackwardsSettlesAtNothingAndSaysSo` | ✅ |
| M6 | вход | `Secure: !c.Insecure` → `Secure: false` | `TestLoginCompletesAndCreatesOurOwnSession` | ✅ |
| M7 | очередь | `phaseBudget` отдаёт весь проход вместо половины | два пина фаз | ✅ |
| M8 | метрики | снят `m.stalledRuns.Set(...)` | носителя нет | ❌ → `PD-389` |
| M9 | метрики | снят инкремент `sweepUnfinished` | `TestTheRunnersStateIsExposedWithItsUnits` | ✅ |
| M10 | метрики | снят `m.abandonedSurfaces.Set(...)` | носителя нет | ❌ → `PD-389` |
| M11 | деньги | `SpendBound`: `min` → `max` | носителя нет | ❌ → `PD-376` |
| M12 | вход | абсолютный потолок сессии умножен на 100 в `CreateSession` | носителя нет | ❌ → `PD-380` |
| M13 | вход | `ClearSession` выдаёт TTL `time.Hour` вместо `-time.Second` | носителя нет | ❌ → `PD-382` |
| M14 | вход | в `set` ветка `ttl < 0` даёт `maxAge = 3600` | носителя нет | ❌ → `PD-382` |
| M15 | вход | срок хранения журнала входов 180 суток → 180 ЛЕТ | носителя нет | ❌ → `PD-383` |
| M16 | вход | предикат свипа журнала обезврежен | носителя нет | ❌ → `PD-383` |
Пол по каждой оси выполнен — колонка «ось». Все девять выживших пере-проверены на ПОЛНОЙ батарее
против чистой базовой линии ТОЙ ЖЕ копии, не по exit-коду.
⚠ **Правило вердикта, которое пере-ранящему знать обязательно:** судить по ДЕЛЬТЕ против чистой
копии И по ТОПИЧНОСТИ упавшего теста, а не по цвету батареи. Цена забывания оплачена здесь дважды:
известный флейк `PD-369` дал ложное «пойман», а первая редакция `mutations-full.log` печатала `RED`
там, где мутация выжила. Правило записано в `ENGINEERING_STANDARDS` §3 п.3, `PD-382` и `PD-395`.
**Реестр против дерева — 17 ⚠-дописок.** Строка `open`, чьё лекарство уже в дереве, отправляет
следующий пак чинить построенное. Предлагаю ЗАКРЫТЬ: `PD-23`, `PD-203`, `PD-166`. Сузить, но НЕ
закрывать: `PD-162`, `PD-368`, `PD-374`. Пере-формулировать: `PD-6`, `PD-157`, `PD-168`, `PD-90`,
`PD-101`. Свести с условием, названным в дописке: `PD-251`↔`PD-42`, `PD-250`↔`PD-179`. Помечены
токеном `ОСПОРЕНО(PD-N)` — статусы паком НЕ менялись: `PD-159`, `PD-293`, `PD-361`. Конвенция
токена записана в шапку регистра.
**Аддендумы владельца по ходу (§5 промта) — три, результат:**
1. «Архитектурно чистое решение по `PD-395`?» → строка ПЕРЕПИСАНА, диспозиция сменилась с «править
гейт» на «править рецепт», рецепт исправлен и проверен исполнением, а лечение переехало из
эфемерного носителя (промт архивируется при лендинге) в `ENGINEERING_STANDARDS` §3.
2. «Посоветуйся со старшим; веди через существующего агента» → два захода старшей модели, оба
абсорбированы; дальше вёл сообщением, а не новым агентом.
3. «Доводи до конца» → рефутер по восьми утверждениям сверки реестра (последний рубеж без
адверсариальной проверки) и редакторский аудит сдачи; оба нашли дефекты, оба закрыты.
⚠ **Мои ошибки, которые нашли не я, и они того же класса, который пак ловил.** (а) Сослался на
`PLATFORM_DIRECTION` §3 как на ратифицированное направление по `oapi-codegen`, не сверив, что оно
ПЕРЕ-ПОДПИСАНО `D39.132` в «кандидат» и P7 решил не брать. (б) Дописка к `PD-166` описывала
механизм, недостижимый в сегодняшнем коде. (в) Сужения четырёх строк оси 34 оказались КРУГОВЫМИ —
каждое опиралось на поверхность, несостоятельность которой доказывает соседняя строка того же пака;
из-за этого `PD-385` стояла minor. (г) `PD-379` несла вес major, а лежала в секции minor. (д) Опись
изменённого называла два файла из пяти — лендер недобрал бы правку НОРМЫ. Всё исправлено; называю,
потому что необъявленная ошибка автора — это находка, которой нет.
**Obstacle.**
· Чужой демон `tmplatformd` pid 211037 (порты 8080/9464, база `tmstand`) — не трогал; пак изолирован
своими портами 8099 и 81018114 и базами `tmp8rev_*`. Соседняя бэкенд-сессия живёт в дереве
(18 файлов) — отсюда транзитивный якорь выше.
· Первый запуск четырёх осей умер на середине (обрыв сессии плюс `Connection lost` у одного агента).
Копии сверены `diff -rq`, базы пере-созданы, всё пере-запущено с нуля.
· **Чего НЕ проверял.** Сырые логи 42 агентских посадок не сохранены — их вердикты пере-ранить
нельзя, только пере-посадить по описанию; из 21 «пойман» 19 называют конкретный тест, 2 нет.
Пере-проверка выживших шла `go test ./...` БЕЗ `-race` (для класса «пина нет» вердикта не меняет,
но сказано). Не пере-мерены мной: «до 17 раз» в `PD-377`, живая проба preflight из `PD-96`,
экономика петли `PD-215`, кардинальность лейблов под нагрузкой. Числа пробы в дописке `PD-162`
сняты рефутером, лог в артефакты не попал. Клейм «$0.15 против $0.03» СНЯТ — лога не осталось,
механизм на его месте доказан грепом.
**Вопросы приёмке.**
1. `PD-159`, `PD-293`, `PD-361` стоят `fixed`, а их пины доказывают меньше. Статусы не менял —
пере-открыть это одна правка, и она ваша; улика и живые пробелы вынесены новыми строками.
2. Правка НОРМЫ (`ENGINEERING_STANDARDS` §3 п.3) и эррата к `STACK_DECISIONS` §13 сделаны мной в
своей зоне, но обе меняют объявленное — нужна ваша ратификация или откат.
3. Снятие галочки `ASVS 5.0 7.4.1` в `docs/archive/platform-PROGRESS-P0-P3.md:572` — там она стоит
выполненной словами «отзыв прекращает использование», что `PD-379` опровергает. Это ЧУЖАЯ зона.
4. Класс «open, а лекарство построено» стоит зоне ~10% открытых строк. Предложение, оба этажа вне
права читающего пака: норма в промты кодовых паков («построил механизм → грепни открытые строки
по именам своих файлов, каждое совпадение — диспозиция в отчёте») и, пингом в `docs/`,
пересечение застейдженных путей с якорями открытых строк.
5. `PD-398`: `malformed()` в `counts.py` ловит только недостачу ячеек — однострочная правка в вашей
зоне.
- **P8-FIX ПРИНЯТ И ЗАЛЕНДЕН оркестратором №18 (22.08).** Тело приёмки — ратифицированная нота
**D39.154** (`docs/architecture/05-decisions-log.md`), здесь не пересказывается. Зоне важно ровно
следующее, и этого в ноте нет:
- **Пинг зоны №1 ЗАКРЫТ:** якорь `05-decisions-log.md:542` → `internal/pgstore/perf_test.go:13` жив,
токен «96% of the page» на месте — правку комментария бенчмарка приёмка приняла.
- **Правку зоной ЧУЖОГО документа** (пере-нацеливание двух моих якорей в промте читающего пака
после переезда констант) **утверждаю и не откатываю**: якоря умерли по вине пака, пак их починил
и раскрыл это сам, вместо того чтобы обойти красный гейт. Это ровно то поведение, которого норма
и требует, — так и делайте дальше.
- **«Скипов 0» у меня НЕ воспроизвелось: три скипа.** Не ваша регрессия и не ошибка отчёта —
`systemdOrSkip` гейтит три теста `internal/runner` достижимостью пользовательского менеджера
systemd, а на моём хосте `/run/user/1000` нет. Незакрытым осталось то, что рецепт и критерий
приёмки объявляли у батареи ДВА гейта, а их три: `PD-374`, оба носителя поправлены 22.08.
- **Мои одиннадцать посадок мутаций вне вашего списка:** убито восемь, выжили три — `StalledAfter`
законно (носитель один), `maxAttempts` и `truncateReason` дали `PD-373`/`PD-372`. Плюс находка
вне карты пака `PD-371`. ⚠ Две первых редакции МОИХ посадок дали ложное «выжила» — гонял не тот
пакет; пере-прогнано. Говорю, потому что необъявленная ошибка харнесса приёмки — находка, которой нет.
- **Числа сдачи в вашем отчёте держатся**, кроме одного: «отпечатков миграций 41» — их 25
(`grep -c '^[0-9a-f]\{64\}' migrations.sha256`); число было названо без команды. Регистр после
приёмки — **374 строки, открытых 72** (1 major, 16 minor, 55 info).
- **Открытым уезжает `PD-370`** (контрактная половина) — не работа зоны, закрывать её здесь было бы
подгонкой под критерий приёмки. Согласен с вашей диспозицией.
⚠ **Порядок паков вышел ОБРАТНЫМ очереди:** промт P8-FIX требовал запуска ПОСЛЕ читающего пака,
запущен был раньше. В плюс — названная цена перестановки («блокер живёт всё время читающего пака»)
НЕ заплачена. В минус — §4.7 релея пуст, и читающий пак пойдёт по только что переписанному коду;
его промт про это предупреждён баннером.
- **Эра пака P8-FIX (2122.08) — В АРХИВЕ.** Отчёт пака, обе волны ревью, спил пер-термной подписи, инвентарь каналов и obstacle — [archive/platform-PROGRESS-P8.md](archive/platform-PROGRESS-P8.md). Ратификация — D39.154, лендинг `31f1f82`. Живое из этой эры: четыре открытые строки регистра (`PD-370` мажор — контрактная половина · `PD-371` · `PD-372` · `PD-373`/`PD-374`), инвентарь каналов шва в `STACK_DECISIONS.md`, граница sqlc в `BACKLOG.md` П-19.
- **Записи акта 5 пака P7, остававшиеся в живом журнале, — ДОСЛАНЫ в [archive/platform-PROGRESS-P7.md](archive/platform-PROGRESS-P7.md)** 22.08: заголовок «эра P7 в архиве» стоял, а тела лежали здесь.
- **Запись оркестратора, 15.08 — P6 + ДОФИКС ПРИНЯТЫ И ЗАЛЕНДЕНЫ (D39.132).** Приёмка двумя
раундами: панель 5 адверсариальных верификаторов по P6 (две линзы — другой моделью; исполнением,
включая живой демон и живой PG) → фикс-лист ФП-1…ФП-8 → дофикс → финальная верификация
исполнением: батарея `make check` под `-race` с живым PG и ОБОИМИ гейтами пере-прогнана
оркестратором — EXIT=0, 17 пакетов, скипов 0; тестов **416** (`grep -rh '^func Test' platform
--include='*_test.go' | wc -l`; HEAD = 359); деньги пере-считаны из сырого леджера стендовой БД
двумя путями (сошлись до цента); живая проба рендера пере-снята своим tmctl из HEAD; 2
собственные посадки приёмки на дофикс — обе пойманы (`TestAFlaggedBookStillAnswersAboutItsMoney`
· `TestEachOfTheThreeBlockersAloneKeepsABookOutOfTheMigrationList` — вторая до дофикса переживала
всю батарею). **Исправлено оркестратором при лендинге:** откачено переименование закрытой строки
PD-198 → PD-199 и удалена заведённая под PD-198 строка-дубль с ложным обоснованием (регистр
246 → 245 слиянием; «ID стабилен навсегда»); PD-200 перенесена в «Закрытые — эра P6», PD-180 —
в «Открытые — info». Ратификации и диспозиция вопроса PD-241 — тело D39.132.
- **ДОФИКС P6 ЗАКРЫТ — дерево передаётся на лендинг одним пакетом с P6 (15.08).** Фикс-лист приёмки
ФП-1…ФП-8, раздел «Дофикс P6» ниже. Ни одна находка не опровергнута, но две подтвердились не так,
как их описала приёмка. Самая тяжёлая — денежная и на ОБЫЧНОМ пути: движок отвечает exit 2 из
`status --json` про любую книгу с помеченной единицей, а платформа читала это как «движок не
ответил», из-за чего расчёт такого прогона откладывался вечно и следующий прогон книги не
стартовал. Плюс аддендум владельца 15.08: `day_usd` убран из ПРИМЕРА шаблона книги. Батарея под
`-race` с обоими гейтами: EXIT=0, 17 пакетов, скипов 0, линтер 0 issues, `make vuln` чист,
**359 (HEAD) → 416** тестовых функций — счёт командами в разделе. Посадки 15 из 15.
- **P6 ПОСТРОЕН — дерево передаётся на лендинг (14.08).** Потребительская половина шва эмиттера
(П-15) · интейк формы Б (П-14) · дев-стенд с сидом и дев-входом (П-16) · порядок апгрейда движка
против деадлока v15 · попутные PD · гигиена зонных доков. **PD-113 закрыт — открытых major в
регистре НЕТ.** Батарея на стенде `~/.local/pgsql` (порт 55433) под `-race`: EXIT=0, 17 пакетов,
линтер 0 issues, скипов 0, `make vuln` чист, тестов зоны **359 → 401** (числа P6; счёт — команды
в разделе «Дофикс P6», прежний базис «396 → 403» не воспроизводился, PD-234). Посадки:
20 из 20 в первом круге (три переписаны после разбора переживших) + 6 из 6 на находки
адверсариальной панели, одна названа НЕПИНЯЕМОЙ по построению. Живые пробы на настоящем `tmctl`
и на фейке движка с правилом потолка — разделы ниже.
⚠ **Три вопроса на ратификацию оркестратору** (подробности в разделе P6): внутренняя причина
`daily_ceiling` НЕ проецируется на провод (контракт знает одно значение — PD-199, спек-правка
за S4) · направление `PLATFORM_DIRECTION` §3 предложено ПЕРЕ-ПОДПИСАТЬ (oapi-codegen и sqlc не
исполнены, срок «до первого хендлера» пройден) · батарея получила ВТОРОЙ гейт окружения
(`TM_PLATFORM_TEST_ENGINE_BIN` + `_BOOK_TEMPLATE`), без него один тест честно скипается.
- **P5 ПОСТРОЕН и ДОФИКШЕН по приёмке — дерево передаётся на лендинг (11.08).** *(→ принят в три раунда и заленден D39.130, `69d485a` — запись оркестратора 14.08 ниже.)* `POST /v0/books`
потоково с пер-маршрутным потолком тела и своим дедлайном чтения · статусы
`uploading parsing not_started | rejected` получили писателей, разбор — $0-команда движка
`tmctl manifest` · `POST /v0/runs/{id}/stop|resume` с намерением стопа в Postgres ДО сигнала ·
метрики Prometheus на отдельном слушателе · печать эффективной конфигурации.
- **Приёмка 11.08 (8 находок) → дофикс → кросс-семейное ревью дофикса (5 находок) → ре-чек
оркестратора (3 находки, 2 high) → ТРЕТИЙ РАУНД ЗАКРЫТ (14.08)**: разделы «Третий раунд»,
«Дофикс-2» и «Дофикс P5» ниже, движение — записками-планами. Батарея на стенде `~/.local/pgsql`
(порт 55433) под `-race`: EXIT=0, 16 пакетов, линтер 0 issues, скипов 0, `make vuln` чист, тестов
**261 → 359**. Посадки: 10/10 · 6/6 · 6/6 (две пережившие за все раунды — оба раза виноват был
слепой пин, переписан пин, не мутация); списки — в журнале.
⚠ **Тулчейн 1.26.6 НА РАТИФИКАЦИЮ** *(→ ратифицирован D39.130)*, и вчерашний клейм про него был ЛОЖЕН: гейт принимал 1.26.5
(регекс вместо сравнения). Теперь floor держат два механизма — `make version-check` сравнивает
версии, `go.mod` несёт `toolchain go1.26.6`, который читает всякая сборка; при `GOTOOLCHAIN=auto`
хост скачает нужный тулчейн, при `=local` остановится. Само сравнение запинено (`internal/gates`).
**ВОПРОС на ратификацию — кто пишет `book.yaml` при интейке**: половина П-9 стоит на нём, раздел
«Развилка» ниже. *(→ решено D39.130: бета = форма Б, стройка = П-14; движковая форма В = строка 170 единого.)*
- **Запись оркестратора, 14.08 — P5 ПРИНЯТ И ЗАЛЕНДЕН (`69d485a`, D39.130).** Финальная
верификация исполнением: батарея EXIT=0 на том же стенде · **гейт версий живьём отказывает
1.26.5 и `go1.27rc1`, принимает 1.26.6 и 1.26.10** (sort -V честный) · `toolchain go1.26.6`
в go.mod · сентинел `.tmplatform-books` на месте · `revision+1` остался ровно у двух заявленных
прогресс-писателей синка (мотивированное исключение ратифицировано) · тестов **359**, регистр
**198**, удалённых имён тестов против HEAD нет. **Ратифицировано (D39.130):** Go-floor 1.26.6
с toolchain-директивой (цена офлайн-хоста с `=local` названа и принята) · `book.yaml` при
интейке — форма **Б** (рендер из деплой-шаблона; интерпретация D39.110 §2b с аудит-следом,
стройка — следующим касанием зоны), форма **В** (`tmctl init`) — строка 170 единого бэклога
движку · контракт-диспозиции PD-172/173/174/180 — спек-правкой 0.2.3 задачей S4-промта ·
PD-196 → дописка строки 165 (различимость классов отказа `manifest`).
- **Пинг оркестратора, 14.08 (второй) — ЭМИТТЕР ДВИЖКА ЗАЛЕНДЕН (D39.131, `9cfe080`): `events.jsonl` начнёт появляться в каталогах книг.** Словарь = ваш `events.go` с диффом движка (принят приёмкой): `StreamVersion` **1.1** · `Ceiling.Scope: book|day` · `Finished.outcome += ceiling|stopped` · `eta_seconds` = темп ТЕКУЩЕГО прогона · `unit_done.unit` = лидерный `first_chunk_idx`. Ваша половина — **П-15** зонного бэклога (маппинг exit 4/5/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](archive/platform-PROGRESS-P0-P3.md).
Стек и рецепт стенда — `STACK_DECISIONS.md`; критерии приёмки — `ENGINEERING_STANDARDS.md`.
*(Записи паков P4P6 с дофиксами (0815.08) — в [archive/platform-PROGRESS-P4-P6.md](archive/platform-PROGRESS-P4-P6.md); вынесено 16.08 по слову владельца.)*
## Эра пака P7 (1620.08) — в архиве
Пять актов, приёмка, фикс-раунды и таблицы селф-ревью выселены срезом в [`archive/platform-PROGRESS-P7.md`](archive/platform-PROGRESS-P7.md) (норма: закрытая эра не живёт в журнале). Итог и то, что пережило пак, — шапка выше и пинг оркестратора №18 ниже.
## Закрытые эры P0P3 — в архиве
Разделы сессий P0P3 и их ратификаций (0408.08) вынесены в
[archive/platform-PROGRESS-P0-P3.md](archive/platform-PROGRESS-P0-P3.md) (D39.124).
Решения оттуда живут в D-логе и `DEFECT_REGISTER.md`.
## Пинги оркестратора (живые ссылки для следующих сессий)
**Пинг оркестратора №15 (09.08, D39.122): движковый пак «блокеры контракта» ПРИНЯТ и заленден `0e69bc1` — пять поверхностей для платформы существуют. ФИНАЛЬНЫЕ формы (менялись трижды за приёмку — старые в переписке игнорировать):**
- **Манифест глав:** `<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 --json` → `progress: {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): `SignupGrantMicroUSD` → **0** на бете, начисление руками; возврат $5 — вместе с суточным агрегатным потолком, когда появятся платежи. Исполнение — P7-однострочник конфига + тест (грант при неверифицированном email уже 0, `login.go:306-308`). Обновите регистр.
2. **Ваша половина батча 0.3.0 — ПОСЛЕ лендинга спеки; состав — §5 отчёта:** `wireProgress` → один счётчик (Б-0; ваши колонки и словарь шва НЕ трогаются — фазы остаются внутренним делом); словарь машинных `code` + `request_id` в `Problem` (Б-1/§8а: класс 1 — конкретика максимальная, класс 2 модельный — ОДИН грубый код без вариации); `GET /capabilities` (Б-2: пары/порог интейка/форматы/страницы/версия — всё уже есть в конфиге и константах); `WWW-Authenticate` на 401 и две причины 403 (Б-15); трейлинг-части интейка → 400 вместо тихой потери + `Location` на 201 + `Idempotency-Key` (Б-3); подрезание `limit` до максимума вместо сброса к дефолту (Б-10, `books.go:509-511`); `PATCH /books/{id}` title-only + `DELETE` + `GET /runs/{id}` + `Run.failure_reason` (Б-5/Б-8 — словарь исхода у движка уже есть); `blocked: {code, book_id}` на run-options/409 (В-6); отказ по неподдерживаемой паре кодом на интейке (сегодня ja/en-книга умирает `failed`-ом на валидации конфига движка в КОНЦЕ пути — денег не сгорает, но причина не доезжает).
3. **Сеть (§5б; вход до первой живой книги под фронтом, строка 186):** gzip на текстовых ответах (НЕ на SSE) и `ETag`/`If-None-Match`→304 на списочных GET — ваше же требование `PLATFORM_DIRECTION.md:202-205`, батчем оно уезжает В КОНТРАКТ. Числа: дерево 2283 глав = 250 КБ на кадр `status`, фокус-рефетч 12 вкладок = 562 КБ.
4. **К-10 (пофазность у главы): НЕ СТРОИТЬ** — поправка приёмки: вердикт §6 отчёта («правка проекции») противоречит Б-0; фазы уходят с провода и у главы.
5. **Попутные находки §9** (сессия их адверсариально НЕ судила — проверьте у себя, заведите П/PD-строки по месту): (а) расчёт может висеть навсегда в двух легаси-путях (`reconcile.go:660-670` — попытка без базовой отметки; дев-путь без движка) — бюджета «сколько холд может висеть» не существует; (б) флаг «аккаунт остановлен» выводится сканом последних прогонов ВСЕХ книг (`pgstore/books.go:720-724`) — одна книга зажигает аккаунт; после PD-203 перечитать; (в) `ReadRunForSpawn` линеен по живым прогонам (`pgstore/runs.go:712`); (г) `/metrics` без аутентификации, защита — только 127.0.0.1 (`main.go:200-202`); (д) лимитер входа один на процесс (`login.go:128-129,165`) — один клиент упирает вход всем (выбор объяснён комментарием, следствие стоит записать); (е) сырой Go-текст ошибки лежит причиной карантина в БД (`reconcile.go:320`) — на провод не идёт. Вопросы — через владельца.
**Аддендум к пингу №17-третьему (16.08, свип планировочных доков D39.139):** (1) `docs/PLATFORM_DIRECTION.md` §2 и `BACKLOG.md` П-7 держат «фри-тир дефолт $5» — ПРОТУХЛО против PD-104 (слово владельца 16.08: грант 0 на бете, начисление руками; D39.138 п.2л) — поправить тексты при P7. (2) Баннер §3 PLATFORM_DIRECTION («ратификация за оркестратором») закрыт ещё D39.132 п.2б: oapi-codegen — кандидат при P7, sqlc — привязан к P7, River-факт поправлен; баннер снять/заменить ссылкой на ноту. (3) §4 п.5 («сервер ВПРАВЕ склеивать события») сузится батчем 0.3.0: склейка только снимкам, `note` не склеивать (research/28 Б-6в). Вопросы — через владельца.
**Дозакладка к §4.6 промта P7 (16.08, аудит готовности зоны; релей владельца — подтверди эхом):** к списку протухших текстов зоны добавь: `PLATFORM_DIRECTION.md` §4 п.8 («живого канала до-прогонных состояний нет») — решено книжным SSE 0.3.0, опрос запрещён нормой · §4 п.10 («персист манифеста — недостающий») — построен D39.122 · `:184` ссылка на несуществующий §3.3 стандартов · `platform/README.md:3-4` шапка «зона на P1» и «один тест-гейт» (гейта два) · `BACKLOG.md` П-17 — состав пака у́же промта (промт первичен). Расхождение ENGINEERING_STANDARDS:59 против DIRECTION:128 по oapi-codegen решаешь ты (промт §4.6). Всё — попутно, не отдельным заходом.
## Пинг оркестратора №18 — 20.08.2026 (P7 акт 5 ПРИНЯТ С ФИКС-ЛИСТОМ и ЗАЛЕНДЕН; контракт 0.4.0 ратифицирован; тело — D39.152/D39.153)
**Вердикт: ПРИНЯТЬ С ФИКС-ЛИСТОМ.** Панель шести линз в изолированных копиях (слепая · контракт-конформность · деньги · шов · вне карты · ревью канона 0.4.0 другой моделью) + пере-раны и собственные посадки оркестратора. Регрессов против HEAD нет, ни одной линзы с REJECT.
**Пере-проверено МОЕЙ рукой (заявление = команда):** `make check` с обоими гейтами — 18 пакетов, EXIT=0, **скипов 0**, линтер 0 issues · реестр 327 строк / 65 открытых (13 minor, 52 info), major и BLOCKER нет — `python3 docs/scripts/counts.py --check` · оба док-гейта зелёные · `tmplatformctl books --migratable` живой (exit 0, «resumable run» на стендовой книге) · миграционный манифест сходится, released-миграции не тронуты, комментарий про пере-отпечаток 00016 честный.
**Замер, обосновавший разворот 00016→00022, пере-выведен независимо** (свой бенчмарк на трёх вариантах в копии дерева): джойн **14.6 мс** · счётчик **5.3 мс** · без счётчика **2.9 мс**. Порядок и вывод подтверждаются, разворот законен. ⚠ Процент назван в ТРЁХ носителях тремя разными числами (журнал «83%», `perf_test.go:13` «96%», миграция 00022 «16.8 против 3.4, джойн сам по себе 9 мс»); мой замер даёт ≈80%, а «96%» ни из чего не выводится — один носитель на факт, остальные указывают.
**Собственные посадки мутаций ВНЕ вашего списка — 8, поймано 7:** долг не поставлен третьей концовкой ✅ · `edit_wave` не монотонен ✅ (⚠ сообщение пина печатает указатель вместо значения — `readmodel_test.go:918`) · снят `Vary: Accept-Encoding` ✅ · HEAD лишён валидатора ✅ · `reopen` снимает стоп банка кому угодно ✅ · погашение долга без сверки метки ✅ · вынос метки долга из закрывающей транзакции во второй оператор ✅ (техника `xmin` §36 работает, проверено исполнением).
⚠ **НЕ поймано — дыра:** снятие `structure_version` И `revision` из `emitFrame` (`pgstore/events.go:72-73`) проходит ВСЮ батарею. Это два поля, которые канон требует на КАЖДОМ кадре (`EventBase`), и ваш же комментарий это утверждает. По вашей норме PD-1 свойство без пинящего теста считается НЕ закрытым.
### Фикс-лист приёмки — завести строками СВОЕГО бэклога и регистра (зона моя не пишет)
⚠ **ПЕРВЫМ — не из пака P7, но найдено вторым рубежом приёмки и проверено мной построчно: `Sweep` останавливается для ВСЕЙ инсталляции на двух медленных прогонах, и выхода нет.** `cmd/tmplatformd/runner.go:189` — `sweepBudget = 2 * time.Minute` на ВЕСЬ проход; `internal/runs/reconcile.go:70` — `defaultRunBudget = 60 * time.Second` на ОДИН прогон. 120/60 = 2: два прогона, выбравшие свой бюджет, съедают проход целиком, и тогда `ctx.Err()` (`reconcile.go:39,48`) обрывает цикл — **`UnsettledRuns`, единственный ретрай отложенного расчёта, не вызывается вообще**, а живые прогоны, стоящие ниже, не реконсилируются и не спавнятся. `ListLiveRuns` сортирует `order by r.started_at` (`pgstore/runs.go:249`), то есть заклиненный прогон — по построению самый старый — стоит в голове и голодит остальных КАЖДЫЙ проход, детерминированно. Ручки нет: `runs.Config.RunBudget` объявлен (`internal/runs/runs.go:62-65`), но `startRunner` его НЕ присваивает (`cmd/tmplatformd/runner.go:74-83`), переменной окружения нет ни для одного из двух чисел, а `tmplatformctl` не умеет ни закрыть прогон, ни вернуть холд. Цена: прогон вечно `finished_at is null` ⇒ холд не возвращается ⇒ `runs_one_live_per_book` не даёт запустить новый прогон этой книги. Деньги заморожены, книга заморожена, пользователю видно «идёт».
**Достижимо буднично, без экзотики:** `RunSink.Apply` берёт транзакцию с блокировкой книги на КАЖДУЮ строку журнала (`pgstore/sink.go:80-104`), поэтому после часа простоя демона две книги с бэклогом выбирают проход целиком — и всё время догона деньги всей инсталляции не считаются.
⚠ **`PD-169` в вашем регистре стоит `fixed(P5)` и этим ЛЖЁТ приёмке:** его пин гоняет `Sweep` вообще без дедлайна прохода, то есть доказывает пер-прогонный бюджет, а не выживание прохода. Пере-открыть. Код приехал в P6 и в дифф P7 не входил (`Sweep` против HEAD — identical), поэтому ни одна диффовая линза его увидеть не могла: тот же класс «композиция известных фактов», что и строка 198. ⚠ Наблюдаемость при этом ЕСТЬ и она ваша: `tm_platform_sweep_unfinished_total` растёт — то есть оператор увидит, что проход не дошёл до конца, и не сможет ничего сделать. Метрика без ручки — половина механизма.
1. **`ContractVersion` = "0.3.0" при формах 0.4.0** (`internal/httpapi/capabilities.go:13`, он же в каждом `hello` — `stream.go:100`). Ратификация 0.4.0 состоялась (D39.152) ⇒ константа обязана подняться. Комментарий над ней сам это требует: «raised in the same commit as the code that implements a new minor». Сегодня практического вреда нет (фронт заморожен на 0.2.3), но деплой объявляет версию, которую не отдаёт. **Первым пунктом.**
2. **Пины на два поля `EventBase`** (см. выше). ⚠ Попутно 21.08 усилен гейт якорей доков: `file:line` теперь может нести токен ожидания (`` `путь:12-14`=`подстрока` ``), и такой якорь сверяется ПО СОДЕРЖИМОМУ на каждом прогоне, включая коммиты, которые двигают ЦЕЛЬ. Ваши доки (`platform/docs/**`) вошли в область линта — раньше их не сканировал никто.
3. **Бюджет попыток материализации.** У долга нет предела и канала «признать безнадёжным»: claim → fail → defer(`now()`) → следующий свип через 15 с, вечно (`pgstore/books.go:538,557-564`). У интейка предел есть (`parseAttempts`=5). Три следствия: до 5 мин движковых процессов на проход бесконечно · поток такой книги НЕ заканчивается никогда (`AtRest` требует `read_model_owed_at is null`, `events.go:125`) · при живом манифесте и падающем экспорте `SaveStructure` коммитится каждый проход ⇒ `revision++` и кадр каждые 15 с.
4. **sqlc — БЕРЁМ, слово владельца 20.08 («я вообще за»).** Отступление P7 закрыто, PD-44 переоткрыть исполнением. ⚠ Ваше предложение «взять на однооператорных ручках P8» покупает инструмент туда, где не болит: рантайм-ошибки «нет такой колонки» случились в СКЛЕЕННОМ SQL read-модели (⚠ счёт: проверяемый носитель — ваш регистр PD-44 и журнал — несёт ДВЕ; ещё две вы назвали в ответе владельцу, в доки они не попали, поэтому опираемся на две — испр. 20.08) (`chapters_before`/`stop_for_signing` — `readmodel.go:441,446`, там же `note_count` и сломанный алиас `b`), куда sqlc по построению не дойдёт. Тем же паком — гейт, который туда дойдёт: прогонять КАЖДЫЙ собранный запрос через разбор Postgres (prepare/describe) против мигрированной схемы; склейка ему не мешает, он получает финальную строку. И ответить попутно на вопрос, которого мы не знаем: **есть ли в read-модели запрос, которого не касается ни один тест** — если есть, это не «медленная обратная связь», а дыра. ⚠ **Половина ответа УЖЕ получена вторым рубежом приёмки** (собранный SQL извлечён из пакета через `go/types` и прогнан `EXPLAIN (GENERIC_PLAN)` против мигрированной схемы): **141 запрос, все планируются чисто, остаточных «нет такой колонки» НЕТ**; тестом ЭТОГО пакета недостижимы десять, но по батарее ЦЕЛИКОМ реально не покрыты только **три** (`DeleteOldLoginEvents` · `UserByIdentity` · `Observe`; остальные семь покрыты на 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.json` — `backend/internal/pipeline/artifact.go:24-59`); `bank-stop.txt`, `mined-signature.yaml`, `auto-bank.yaml` пишутся `os.WriteFile` с усечением. Сегодня безвредно (читателей нет), но сессия, взявшая любой из них по той строке, получит усечённый документ на живом прогоне.
**Сессия P7 ЗАКРЫТА владельцем 20.08.** Промт отработан → `platform/docs/archive/`. Дальше — другие сессии.