From eae664f6b4828065807fd9ab92e1cffae0428100 Mon Sep 17 00:00:00 2001 From: heaven Date: Wed, 2 Sep 2026 19:39:04 +0300 Subject: [PATCH] Final documentation pass in the platform zone: keep what a session needs to work, drop the account of how we got there, and correct the stale claims about code --- platform/BACKLOG.md | 2 +- platform/README.md | 4 +- platform/deploy/README.md | 8 +- platform/docs/DEFECT_REGISTER.md | 2 +- platform/docs/ENGINEERING_STANDARDS.md | 4 + platform/docs/PLATFORM_DIRECTION.md | 21 +- platform/docs/STACK_DECISIONS.md | 11 +- platform/docs/platform-PROGRESS.md | 683 ++++++++++--------------- 8 files changed, 294 insertions(+), 441 deletions(-) diff --git a/platform/BACKLOG.md b/platform/BACKLOG.md index 81900135..92d421f5 100644 --- a/platform/BACKLOG.md +++ b/platform/BACKLOG.md @@ -31,6 +31,6 @@ | П-16 | **ИСПОЛНЕНО P6 (14.08).** Дев-сид стенда: тестовый пользователь, чтобы фронт разрабатывался на живой платформе, а не на своих моках (слово владельца 14.08). Границы: фикстуры фронта остаются батарее его гейтов (детерминированные состояния — не работа стенда); полный снос `frontend/src/mock/` — триггер Ф-29, после читающей поверхности | ИСПОЛНЕНО P6: `tmplatformctl seed` и дев-вход `TM_PLATFORM_DEV_LOGIN` с четырьмя гардами непроходимости в проде — рецепт и разбор гардов в `deploy/README.md` §«Дев-стенд». Прогнано на стенде целиком | владелец 14.08 | | П-17 | **ИСПОЛНЕНО P7 (16–17.08).** Читающая поверхность — остаток П-1, названный отдельной строкой | ИСПОЛНЕНО P7 по канону 0.3.0; состав поверхности — `platform/README.md` §«Что здесь будет» и §«Карта зоны» (`internal/readmodel`). ⚠ `submitBankDecisions` СНЯТ 22.08 вместе с пер-термной моделью подписи — D39.144, слово владельца; см. PD-370. **НЕ вошло и отложено в P8:** `updateBook`/`deleteBook`/`getRun`, экспорт (`createExport`/`getExport`), эскроу (П-18) | П-1, D39.84/85, контракт 14 | | П-18 | **Эскроу денег шва: write-ahead intent · `uncertain` · `closing`** (строка 136 единого бэклога) — денежный промт, сознательно НЕ взятый ни P5, ни P6. Здесь же закрывается PD-154 (`settled_at` между `Settle` и `MarkSettled`): половина эскроу рядом с проектируемым целым — второй, более слабый ответ на тот же вопрос | денежный промт | строка 136, PD-154 | -| П-19 | **Конверсия свободного от склейки блока под `sqlc`** (решение владельца 20.08 «БЕРЁМ»; граница названа P8-FIX, PD-44). Пять файлов без единой склейки — `credits.go`(15) · `identity.go`(13) · `idempotency.go`(7) · `sessions.go`(5) · `observe.go`(1) = **42 места / 41 текст / 40 КОНВЕРТИРУЕМЫХ** (⚠ испр. D39.172: `observe.go` структурно не конвертируется — River мигрирует `river_job` сам; худший позиционный дрейф набора, семь `int64` подряд, остался рукописным): это весь кусок пакета, где инструмент силён и ничего не ломает. Остальные 25 склеенных мест недостижимы по построению (`lastRun` — девять потребителей, `nextRevisionOfThisBooksLibrary` — восемь), и это ровно та часть, где рантайм-ошибки и случались. Состав пака: `sqlc.yaml` + пин версии инструмента (как у `golangci-lint`) + генерённый код в дереве + гейт «сгенерённое актуально» + `WithTx` для одиннадцати денежных запросов, идущих внутри ЧУЖОЙ транзакции. ⚠ Класс «нет такой колонки» УЖЕ закрыт постоянным гейтом (`TestEverySQLStatementParsesAgainstTheMigratedSchema`, 162 оператора, включая склеенные), поэтому этот пак покупает типизированные скан-структуры и раннюю обратную связь, а не корректность. **РЕШЕНО владельцем 22.08: ОТДЕЛЬНОЙ СЕССИЕЙ, не внутри пака** — изменений много, мешать их с чем-либо нельзя | отдельная сессия | PD-44, фикс-лист приёмки P7 п. 4, решение владельца 22.08 | +| П-19 | **Конверсия свободного от склейки блока под `sqlc`** — ⚠ **ИСПОЛНЕНО отдельной сессией 29.08 и заленджено (D39.172; `PD-44` закрыт).** Отдельной сессией, а не внутри пака, — решение владельца 22.08: изменений много, мешать их с чем-либо нельзя. Что построено (пин v1.31.1, `sqlc.yaml`, генерённый код в дереве, два гейта актуальности, override `*.*_micro_usd` на деньги) — `docs/STACK_DECISIONS.md`, строка «Кодоген SQL». ⚠ Живая ГРАНИЦА набора, которую стоит помнить при следующем касании: **42 места вызова / 41 текст SQL / 40 конвертируемых**; `observe.go` структурно не конвертируется — River мигрирует `river_job` сам, и внесение чужой схемы в конфиг завело бы ВТОРОЙ её носитель (испр. D39.172), поэтому худший позиционный дрейф набора — семь `int64` подряд — остался рукописным. Остальные 25 склеенных мест недостижимы по построению (`lastRun` — девять потребителей, `nextRevisionOfThisBooksLibrary` — восемь), и это ровно та часть, где рантайм-ошибки и случались. ⚠ Пак покупал типизированные скан-структуры и раннюю обратную связь, а НЕ корректность: класс «нет такой колонки» закрыт постоянным гейтом `TestEverySQLStatementParsesAgainstTheMigratedSchema` (пол — 140 операторов; после конверсии видит 172 — числа в `PD-44`) | ИСПОЛНЕНО (D39.172) | PD-44, фикс-лист приёмки P7 п. 4, решения владельца 20 и 22.08 | | П-20 | **Хвост PD-162 после P8-FIX: холд прогона, чей движок не ответит НИКОГДА, возвращается только рукой.** Пак дал оператору и диагноз (`tmplatformctl runs --stalled`, гейдж `tm_platform_runs_stalled`), и терминальный вердикт (`run abandon [--release-hold]`), и это сознательная граница: автоматически решать про деньги прогона, о котором нельзя спросить, — та же ошибка «я не смог спросить = его нет», только со счётчиком впереди. Автоматический ответ на этот вопрос и есть эскроу — write-ahead intent · `uncertain` · `closing` (строка 136 единого бэклога, П-18). Пока эскроу нет, ручка остаётся ручной, и это записано, а не подразумевается | вместе с П-18 | P8-FIX | | П-21 | **Ключ идемпотентности под конкуренцией даёт 500 (PD-369).** ⚠ Не работа пака P8-FIX и файл им не тронут — найдено попутно, прогоном батареи: собственный тест `TestAClaimThatLostARaceToAReleaseIsRetriedAndNotAnError` ФЛЕЙКОВЫЙ, 1–2 падения на ~80 прогонов. Причина не в тесте: `ClaimIdempotency` ретраит проигранную гонку ровно `claimRounds = 3` раза, а восемь одновременных попыток одного ключа, каждая из которых сразу отдаёт ключ назад (как делает любой 4xx), могут отобрать гонку у одного проигравшего трижды подряд — и он получает внутреннюю ошибку вместо одного из четырёх контрактных ответов. Направление, а не решение: граница по ВРЕМЕНИ вместо числа раундов либо `ErrKeyInFlight` при исчерпании — это решение о контрактном поведении | до первого чужого пользователя | попутная находка сессии P8-FIX | diff --git a/platform/README.md b/platform/README.md index 929f14e4..70ab1454 100644 --- a/platform/README.md +++ b/platform/README.md @@ -110,7 +110,7 @@ Prometheus; на контрактную поверхность они не вы |---|---| | `cmd/tmplatformd` | демон: сборка зависимостей, очередь, свипы. `runner.go` — единственное место, где зона склеивается | | `cmd/tmplatformctl` | админ-CLI и `exit-marker`, который systemd зовёт на конце прогона | -| `internal/httpapi` | ВСЯ контрактная поверхность: `v0.go` (маршруты, библиотека, интейк, прогоны) · `reading.go` (главы, пары, замечания, банк) · `stream.go` (SSE) · `capabilities.go` · `problem.go` (модель ошибок) · `conditional.go` (ETag/304 + gzip; SSE через него НЕ проходит — потому и не сжимается) · `idempotency.go` · `project.go` и `reading.go` (переводы словарей: read-модель → провод) | +| `internal/httpapi` | ВСЯ контрактная поверхность: `v0.go` (маршруты, библиотека, интейк, прогоны) · `reading.go` (главы, пары, замечания, банк) · `stream.go` (SSE) · `capabilities.go` · `problem.go` (модель ошибок) · `conditional.go` (ETag/304 + gzip; SSE через него НЕ проходит — потому и не сжимается) · `idempotency.go` · `project.go` (переводы словарей: read-модель → провод) · `bank.go` (дверь правок банка) | | `internal/auth`, `internal/login` | сессии, CSRF, вход. Принципал создаётся ТОЛЬКО в мидлваре | | `internal/books` | интейк: приём файла, каталог книги, рендер стартового `book.yaml`, разбор через `tmctl manifest` | | `internal/runs` | жизнь прогона: допуск, спавн транзиентного юнита, реконсилятор, деньги на границе попытки | @@ -121,7 +121,7 @@ Prometheus; на контрактную поверхность они не вы | `internal/pricing`, `internal/money` | шкала глав и целые микро-доллары | | `internal/metrics`, `internal/reqid`, `internal/jobs`, `internal/config`, `internal/gates` | телеметрия, id запроса, очередь, конфигурация, гейты тулчейна | -Каналы движка, которыми зона пользуется, — СЕМЬ: `tmctl manifest --json` · +Каналы движка, которые зона ЗНАЕТ (не все потребляются — см. ниже), — СЕМЬ: `tmctl manifest --json` · `tmctl export --json --pairs` · `.bank.json` · `tmctl status --json` · `events.jsonl` · `tmctl build` · `tmctl bank-apply`. ⚠ Слов «и других нет» здесь нет намеренно, и второй копии перечня здесь тоже нет: атомарность каждого канала, колонка «потребляет ли платформа» и все diff --git a/platform/deploy/README.md b/platform/deploy/README.md index 173db639..9fc66835 100644 --- a/platform/deploy/README.md +++ b/platform/deploy/README.md @@ -379,11 +379,9 @@ mkdir -p "$TM_PLATFORM_BOOKS_DIR" "$TM_PLATFORM_STATE_DIR" await fetch('/auth/dev-login', {method: 'POST'}) ``` -⚠ **Дев-вход не должен доехать до прода, и это гарантировано четырьмя независимыми свойствами** -(разбор — `internal/login/dev.go`): он не монтируется без `TM_PLATFORM_DEV_LOGIN`; вместе с OIDC -демон НЕ СТАРТУЕТ; личность берётся из конфигурации, а не из запроса, поэтому войти «кем-то другим» -через него нельзя в принципе; провайдер называется `dev` и в это пространство имён не может попасть -ни один настоящий издатель. На каждый выданный сеанс пишется WARN. +⚠ **Дев-вход не должен доехать до прода, и это гарантировано ЧЕТЫРЬМЯ независимыми свойствами** — +перечень и довод каждого лежат единственным носителем в `docs/STACK_DECISIONS.md` §30, разбор в +доккомментарии `internal/login/dev.go`. На каждый выданный сеанс пишется WARN. ⚠ **`TM_PLATFORM_BOOKS_DIR` — каталог, который сервис ПИШЕТ и, на отклонённом интейке, УДАЛЯЕТ.** Всё под ним создано загрузкой книг; книга, заведённая дев-инструментом с чужим `--workdir`, туда не diff --git a/platform/docs/DEFECT_REGISTER.md b/platform/docs/DEFECT_REGISTER.md index bdff7784..37a20341 100644 --- a/platform/docs/DEFECT_REGISTER.md +++ b/platform/docs/DEFECT_REGISTER.md @@ -105,7 +105,7 @@ | PD-201 | bug | info | `deploy/README.md`, движок `tmctl migrate` | **Самолечение деплой-деадлока v15 ЖДЁТ движковую половину.** Read-only `status` отказывает файлу проекта старее бинаря, платформа зовёт его перед каждым спавном и на расчёте денег — значит выкат движка запирает книги до `tmctl migrate`. Порядок апгрейда записан и исполним (`tmplatformctl books --migratable` пропускает книги с живыми прогонами, резюмируемыми попытками и незакрытыми холдами — все три блокируют по РАЗНЫМ причинам, аддендум оркестратора 14.08). Чего нет: движковый `migrate` придёт с машиноразличимой ошибкой «версия не совпала», и тогда платформа сможет лечиться сама — поймала → `migrate` (если у книги нет открытых попыток) → повтор. Вслепую не строится: без формы ошибки любой матчер был бы догадкой по тексту. ⚠ Проверка исполнением самого шага `migrate` тоже ждёт и НЕ помечена сделанной ⚠ **ПОЛОВИНА ЗАКРЫТА P7 (проверка исполнением):** рантбук деплоя прогнан end-to-end на стенде живым `migrate` — проектная БД, отведённая на схему v14 при бинаре v15, даёт `tmctl status --json` exit 13 с токеном `schema_mismatch found=14 expected=15`; `tmctl migrate` делает пред-миграционный бэкап и переводит v14 → v15; тот же `status` после — exit 0. Мёртвая цитата ошибки вычищена из `deploy/README.md` и из П-1 бэклога, пример в `tmplatformctl/runs.go` переписан на ВЕРСИОНИРОВАННЫЙ путь бинаря. ⚠ ОСТАЁТСЯ сам автомат самолечения («поймал 13 → migrate → повтор») — он не построен: строить его правильно значит решать, кто имеет право мигрировать книгу с открытыми попытками, а это тот же вопрос, что у `books --migratable` | open | аддендум оркестратора 14.08 (ресёрч деплоя) | | PD-204 | standards | info | `internal/runner/systemd_test.go` мета-пин | **Мета-пин systemd-гейта ПЕРЕЧИСЛЯЕТ формы отказа вместо того, чтобы спрашивать способность.** Хвост (г) третьего раунда, у которого не было носителя. Сам гейт вылечен (PD-178: спрашивает, доходит ли процесс до своего менеджера), а тест, который его сторожит, по-прежнему знает список сообщений — у стража та же болезнь, от которой лечили охраняемого. Принято как ОСТАТОК с названной ценой: systemd меняет тексты между версиями, и мета-пин протухнет молча; вреда сегодня нет, потому что он не гейт, а страж гейта. Заведён по пингу оркестратора 14.08 | open | третий раунд P5, хвост (г) | | PD-205 | hardening | info | `internal/login/dev.go` `login`, `internal/auth/csrf.go` `cookieUnsafe` | **Дев-вход защищён от login-CSRF только ОДНИМ слоем из двух.** `auth.CSRF` требует заголовок `X-TM-Client` лишь на unsafe-запросе, который УЖЕ несёт сессионную куку, а на входе куки по определению нет — значит остаётся только `http.CrossOriginProtection`, и клиент, не присылающий ни `Sec-Fetch-Site`, ни `Origin` (тот самый браузер до 2023, ради которого второй слой и заведён), может кросс-сайтом ввести браузер жертвы в ДЕВ-аккаунт. Последствие на стенде ничтожно — аккаунт один и общий, — но свойство слабее, чем «POST, значит безопасно», и записано, а не подразумевается. ⚠ Тот же класс у боевого `/auth/login` (он вообще GET) и по той же причине; лечится либо требованием заголовка на login-маршрутах, либо явным принятием | open | адверсариальное ревью P6 (кросс-семейное, Fable) | -| PD-212 | bug | info | движок `cmd/tmctl/main.go` `exitCode`, `internal/runs/reconcile.go` `outcome` | **Непойманная паника движка неотличима от «завершено с флагами».** Go-рантайм завершает процесс с кодом РОВНО 2 на непойманной панике, а 2 — это `CompletedWithFlags`, единственный «успешный» код контракта; `recover` в `cmd/tmctl`/`internal/pipeline` отсутствует (грепнуто ревьюером). Платформа читает только `$EXIT_CODE`/`$EXIT_STATUS` и записывает `ready` для прогона, который упал посреди работы. Деньги целы (расчёт берёт цифру из `status --json`), врёт статус. Лечится НЕ здесь: либо `recover` в main движка, либо другой номер для флагов — запрос уходит строкой единого бэклога через оркестратора. ⚠ Платформа МОГЛА бы различить по отсутствию терминальной строки `finished` в журнале, но сознательно не судит прогон по строке, которую крэш обрезает | open | адверсариальное ревью P6 (линза шва) | +| PD-212 | bug | info | движок `cmd/tmctl/main.go` `exitCode`, `internal/runs/reconcile.go` `outcome` | **Непойманная паника движка неотличима от «завершено с флагами».** Go-рантайм завершает процесс с кодом РОВНО 2 на непойманной панике, а 2 — это `CompletedWithFlags`, единственный «успешный» код контракта; `recover` в `cmd/tmctl`/`internal/pipeline` отсутствует (грепнуто ревьюером). Платформа читает только `$EXIT_CODE`/`$EXIT_STATUS` и записывает `ready` для прогона, который упал посреди работы. Деньги целы (расчёт берёт цифру из `status --json`), врёт статус. Лечится НЕ здесь: либо `recover` в main движка, либо другой номер для флагов — запрос уходит строкой единого бэклога через оркестратора. ⚠ Платформа МОГЛА бы различить по отсутствию терминальной строки `finished` в журнале, но сознательно не судит прогон по строке, которую крэш обрезает ⚠⚠ **ПРОТУХЛА 02.09, чиню фактом: ДВИЖОК ЭТО СДЕЛАЛ, и посылка строки («`recover` отсутствует») больше не верна.** `backend/cmd/tmctl/main.go` несёт `exitOf` — `main` идёт под `recover`, паника заворачивается в `obs.NewPanicError` и `exitCode` проверяет `*obs.PanicError` **ПЕРВЫМ**, отдавая **1**, а не 2 (греп `func exitOf` и `case errors.As(err, &panicked)`); волновые воркеры несут свой `recover` на шве (греп `recover()` в `backend/internal/pipeline/waverun.go`). Пины движка — `backend/cmd/tmctl/panic_exit_test.go`; лендинг `f840867`. То есть «паника читается как завершено-с-флагами» больше не воспроизводится. **Статус не перевожу — закрытие есть акт лендинга и лекарство в ЧУЖОЙ зоне**; оркестратору: строка готова к закрытию, либо к пере-формулировке в остаток (различение крэша по обрезанному журналу платформа по-прежнему не строит и строить не собирается) | open | адверсариальное ревью P6 (линза шва) | | PD-214 | bug | info | `internal/ingest/tail.go` `apply` | **Чужая или битая hello-строка в общем журнале книги гасит материализацию СВОЕГО потока.** Версия, непустой `engine_run_id` и `seq == 1` проверяются для ЛЮБОЙ hello-строки ДО того, как код решает, чья она: ветка «not mine» стоит после них. Значит чужой процесс (ручной `tmctl` оператора в каталоге книги, старый мажор, баг чужой сборки) валит `Tail` ошибкой, а `quarantines()` считает её терминальной — проекция здорового платящего прогона уходит в карантин НАВСЕГДА (снятия карантина в дереве нет), свежесть падает на медленный ре-синк. Данные целы, деньги целы. Лечится порядком: при известном `want` чужой id распознаётся ДО валидации хендшейка | open | адверсариальное ревью P6 (линза шва) | | PD-215 | bug | info | `internal/runs/reconcile.go` `restart`, `interruptedBySomeoneElse` | **У петли перезапусков нет ни счётчика, ни бэк-оффа.** Каждый цикл — строка `run_attempts`, три строки леджера (холд/возврат/расчёт), два вызова `tmctl status` и транзиентный юнит. Источник, который на каждой попытке тратит ~0 (движок, мгновенно падающий по внешней причине), крутит это вечно: `remaining` не убывает, значит `exhausted` никогда не наступает. Денег не теряется, но это неограниченная работа и рост таблиц. Класс существовал и до пака (путь ребута, строка 138), а ветка «exit 5 без намерения = прерывание» его РАСШИРИЛА. Лечить счётчиком перезапусков на прогон или бэк-оффом по времени последней попытки | open | адверсариальное ревью P6 (линза денег и гонок) | | PD-216 | bug | info | `internal/runs/reconcile.go` `outcome` (полоса отказов) | **Exit 12 (`project_locked`) закрывает прогон терминально, хотя это единственный класс полосы, который проходит САМ.** Обоснование «отказ воспроизводим по построению, поэтому перезапуск — петля» верно для 10/11/19 и неверно для лока: другой процесс отпустит проект. Денег не теряется (холд возвращается целиком, списывается 0), но прогон убит и пользователь покупает новый. Не исправлено намеренно: перезапуск на 12 без бэк-оффа — это PD-215 в чистом виде, поэтому оба лечатся вместе | open | адверсариальное ревью P6 (линза денег и гонок) | diff --git a/platform/docs/ENGINEERING_STANDARDS.md b/platform/docs/ENGINEERING_STANDARDS.md index 594045c5..1f014269 100644 --- a/platform/docs/ENGINEERING_STANDARDS.md +++ b/platform/docs/ENGINEERING_STANDARDS.md @@ -96,3 +96,7 @@ `platform/`, и в `backend/`; по полным путям — 2, оба настоящие). Ратифицировано `D39.159` п. 7. ⚠ Правило симметрично: якорь строки, УБИТЫЙ твоим переездом, чинит тот, чей переезд его убил, и называет причину — не следующий читающий пак. + ⚠ **Дисциплина самого доказательства грепом** (урок сдачи P8-REVIEW, перенесён сюда 02.09 по + просьбе приёмки): обрезанный по ширине вывод грепа читается как ОТСУТСТВИЕ совпадения — так зона + объявила «в промте совпадений ноль» на строке, которую греп нашёл. Предъявляй ЧИСЛО (`grep -c`) + или полные строки, а не «я посмотрел». diff --git a/platform/docs/PLATFORM_DIRECTION.md b/platform/docs/PLATFORM_DIRECTION.md index 4aaed7e4..fd8790f3 100644 --- a/platform/docs/PLATFORM_DIRECTION.md +++ b/platform/docs/PLATFORM_DIRECTION.md @@ -97,22 +97,15 @@ hosted IdP (связка PII + доступность, а свою сессию ## 3. Стандарты и стек: что взять, что не брать -> ⚠⚠ **ВЕРХНИЙ СЛОЙ — ЧИТАТЬ ПРЕЖДЕ ВСЕГО НИЖЕ. По `sqlc` действует НЕ то решение, что записано -> в этом разделе:** владелец сказал **БЕРЁМ** (`D39.153` п.6а, уточнение `D39.154` п.10), и 29.08 -> пак **принят и заленджен** (`D39.172`). Устройство, пин и цена — `STACK_DECISIONS.md`, строка -> «Кодоген SQL»; граница инструмента и построенная взамен замена (гейт планируемости КАЖДОГО SQL -> пакета, включая склейки) — `DEFECT_REGISTER.md`, `PD-44`. Отказ P7 ниже остаётся как история -> пака, а не как действующее решение. +> ⚠ **По `sqlc` действует НЕ решение P7, записанное ниже:** владелец сказал **БЕРЁМ** (`D39.153` п.6а, +> уточнение `D39.154` п.10), пак отработан отдельной сессией и **заленджен 29.08** (`D39.172`, `PD-44` +> закрыт). Устройство, пин и цена — `STACK_DECISIONS.md`, строка «Кодоген SQL»; граница инструмента и +> построенный взамен гейт планируемости КАЖДОГО SQL пакета (включая склейки) — `DEFECT_REGISTER.md`, +> `PD-44`; состав и границы набора — `BACKLOG.md` П-19. > -> ⚠ **ПЕРЕ-ПОДПИСАНО D39.132 п.2б:** `oapi-codegen` — КАНДИДАТ, решение за паком, который его -> берёт. **Решение P7 по обоим — НЕ БРАТЬ, с доводом, а не молчанием (история пака P7, 20.08):** +> ⚠ **`oapi-codegen` ПЕРЕ-ПОДПИСАН `D39.132` п.2б: КАНДИДАТ**, решение за паком, который его возьмёт. +> Довод отказа P7 (20.08) остаётся действующим аргументом, а не историей: > -> - **`sqlc`.** Читающая поверхность добавила ~15 запросов, и ровно они — те, которые sqlc не -> генерирует: запрос страницы и её ревизии живёт в ОДНОЙ транзакции с курсором, водяным знаком и -> агрегатами первой страницы (`internal/pgstore/readmodel.go`), то есть выигрыш был бы на -> `select … where id = $1`, которых в паке единицы. Цена реальна: денежные типы требуют `overrides`, -> а генерённый слой пришлось бы всё равно оборачивать проекцией. Строка PD-44 тогда осталась -> открытой — это был отказ ЭТОГО пака, а не отмена направления. > - **`oapi-codegen`.** Довод «на P7 ручек станет больше, значит дешевле сейчас» проверен фактом: 20 > операций контракта, из них построено 14, и КАЖДАЯ требует рукописной проекции read-модели в > контрактные словари (`internal/httpapi/project.go`) — генератор даёт имена полей, а переводит diff --git a/platform/docs/STACK_DECISIONS.md b/platform/docs/STACK_DECISIONS.md index 1d9e8d45..788f5b45 100644 --- a/platform/docs/STACK_DECISIONS.md +++ b/platform/docs/STACK_DECISIONS.md @@ -376,15 +376,18 @@ > колонках, которых нельзя получить из кода платформы: **атомарность записи** (какие сайдкары можно > читать на живом прогоне, а какие рвутся) и **какие каналы движка платформа не потребляет вовсе**. > ⚠ Якоря в таблице — на код ДВИЖКА, он живёт своей жизнью: при расхождении первичен код. +> ⚠ **Канал `.bank-stop.json` СНЯТ из таблицы 02.09: его больше нет.** Файл и его +> писатель снесены вместе с пер-термной моделью подписи (**D39.158**); проверено +> `grep -rc 'bank-stop.json' backend/ --include='*.go'` → ноль вхождений. Читателя в зоне у него +> не было никогда, так что ничего платформенного на нём не стояло. | Канал | Писатель в движке | Атомарность | Читатель/писатель платформы | Согласовано? | |---|---|---|---|---| | `.manifest.json` | `pipeline/manifest.go` `writeFileAtomic` | **атомарно** (temp+`Sync`+rename, `pipeline/artifact.go`) | `internal/runner/engine.go` (`tmctl manifest`) → `ingest.DecodeManifest` → `internal/books/parse.go`, `internal/readmodel` | ✅ и с P8-FIX строже: `ingest.Manifest.Whole()` зеркалит `BookManifest.selfConsistent` движка по тем правилам, которые платформа читает (главы, юниты, нумерация). Верхняя граница чанков движка counterpart'а не имеет НАМЕРЕННО — платформа счётчиков чанков не берёт | | `.bank.json` | `pipeline/bankexport.go` `writeFileAtomic` | **атомарно** | `internal/runner/artifacts.go` → `ingest.DecodeBank` → `pgstore.SaveBank` | ✅ | -| `.bank-stop.json` | `pipeline/mining.go` `writeFileAtomic` | **атомарно** | **читателя НЕТ** (грепом по зоне — ноль вхождений) | ⚠ канал существует и не потребляется: полная таблица подписи платформе сегодня не нужна, она проецирует банк | -| `.bank-stop.txt` | `pipeline/mining.go` `os.WriteFile` | **НЕ атомарно** (усечение первым делом) | читателя нет | ✅ **и это важное правило, а не наблюдение: брать его на ЖИВОМ прогоне нельзя** — прочитаешь обрезанный файл без всякой ошибки | -| `.mined-signature.yaml` | `pipeline/mining.go` `os.WriteFile` | **НЕ атомарно** | читателя нет | ✅ то же правило | -| `.auto-bank.yaml` | `pipeline/mining.go` `os.WriteFile` | **НЕ атомарно** | читателя нет | ✅ то же правило | +| `.bank-stop.txt` | `pipeline/mining.go` `os.WriteFile` (греп `bankStopTablePath`) | **НЕ атомарно** (усечение первым делом) | читателя нет | ✅ **и это важное правило, а не наблюдение: брать его на ЖИВОМ прогоне нельзя** — прочитаешь обрезанный файл без всякой ошибки | +| `.mined-signature.yaml` | `pipeline/mining.go` `writeFileAtomic` (греп `signatureMapPath`) | **атомарно** | читателя нет | ✅ ⚠ испр. 02.09: писатель сменился на атомарный, прежняя строка «`os.WriteFile`, НЕ атомарно» была протухшей | +| `.auto-bank.yaml` | `pipeline/mining.go` `writeFileAtomic` (греп `autoBankPath`) | **атомарно** | читателя нет | ✅ то же исправление | | `mined_delta` (путь из `book.yaml`) | **писателя в движке НЕТ** — только читатель `loadMinedDelta`; формат `seed.File` (`terms:`), грузится `membank.LoadGlossarySeed`, `Source` пере-штампуется на `"mined"` | — | **писателя НЕТ и у платформы** | ❌ **разрыв — это строка 199(а) единого бэклога**, развилка ждёт ратификации | | `mined_rejects` | читатель `loadMinedRejects`; формат `rejects: [{src, note}]` — это ФИЛЬТР ПРЕДЛОЖЕНИЙ, в банк не входит | — | писателя нет | ❌ тот же разрыв | | `tmctl bank-apply` + документ решений | зона ПИШЕТ (`internal/runs/bank.go` `decisionsFile` → файл во временном каталоге), движок отвечает отчётом `tm-bank-report-v1` на stdout | документ пишется целиком до вызова; отчёт — одноразовая выдача на вызов | `internal/runner/bankapply.go` → `ingest.DecodeBankReport` → `internal/runs/bank.go` `bankVerdict` | ⚠ ЕДИНСТВЕННЫЙ канал, по которому платформа ПИШЕТ в проект движка, — отсюда и своя дверь (`POST /books/{bookId}/bank/corrections`), и свой класс отказов, и класс `write_incomplete` = exit 15. ⚠ Пост-verb факт этого канала (`bank_moved_at`) пишется на ОТДЕЛЬНОМ, отцепленном контексте — иначе обрыв клиента теряет его навсегда (`PD-425`) | diff --git a/platform/docs/platform-PROGRESS.md b/platform/docs/platform-PROGRESS.md index 7dc09e65..60434447 100644 --- a/platform/docs/platform-PROGRESS.md +++ b/platform/docs/platform-PROGRESS.md @@ -4,6 +4,18 @@ > вопросы, предложения на ратификацию. В `docs/PROGRESS.md` платформа не пишет; оркестратор > читает этот журнал при каждом лендинге зоны (свип «решений владельца» — норма D39.99 п.4). +> ⚠ **ФИНАЛЬНЫЙ ПРОХОД ДОКС-РЕЗА, 02.09 (после дофикса ниже) — только текст, кода не тронуто.** +> Снято: пересказы, исполненные планы и таблицы, чьё содержимое живёт по адресу (посадки мутаций — +> комментарии `Mutation caught:` в самих тестах; числа батарей — `STACK_DECISIONS` «Гейты батареи» и +> `counts.py`; тела приёмок — D-ноты). **Починено фактом три протухших утверждения про код:** +> канал `.bank-stop.json` в «Инвентаре каналов движка» (его больше нет, D39.158) и +> атомарность двух соседних сайдкаров · строка `BACKLOG.md` П-19, читавшаяся как открытый план при +> заленджённом паке `sqlc` · строка регистра `PD-212` (посылка «`recover` в движке отсутствует» +> опровергнута — `backend/cmd/tmctl/main.go` `exitOf` отдаёт панике exit **1**, лендинг `f840867`). +> **НЕ тронуто:** код зоны, миграции, тесты, архив-слайсы, `p8-review/`, `p9/`, закрытые строки +> регистра (их ячейки — улика: имена пинов, вердикты посадок, замеры), открытые строки кроме +> `PD-212`. Гейты после правки: `counts.py --lint` — 0 проблемных якорей, `--check` — литералы сходятся. + > ⚠ **ДОФИКС ДОКС-РЕЗА, 02.09 — правка ЧУЖОЙ зоны по прямой санкции владельца 01.09** (сессия > дофикса по вердикту независимого контролёра пакета `bd2077b..HEAD`). **Тронуто, только текст:** > этот журнал · `DEFECT_REGISTER.md` (PD-203, PD-122) · `STACK_DECISIONS.md` (§«Redis нет», §9) · @@ -137,23 +149,19 @@ reads as coverage» — и сам её не исполняет до конца: **3 (major, регистр) — `PD-403` ссылалась на пин, которого нет.** Имя снято; в строке прямо написано, что теста с таким именем в зоне нет ни одного и что свойство несёт открытая `PD-435`, а не пин. -**4 (minor) — шапка против тела.** Старый тест заменён целиком (п.1), противоречия не осталось. ⚠ Но -моя же вставка при этом УКРАЛА док-комментарий у `TestTheBarIsOneMonotonicFractionThroughTheSigningStop` -— тот же класс, что п.6; возвращён на место. +**4, 6, 7 (minor, механика — результат в дереве).** Старый тест заменён целиком (п.1), противоречие +шапки с телом ушло вместе с ним. Четыре УКРАДЕННЫХ моими вставками док-комментария возвращены своим +функциям (`ingest.Manifest.Whole()`, `pgstore.TestAClaimThatLostARaceToAReleaseIsRetriedAndNotAnError`, +`books.TestAManifestThatContradictsItselfNeverCostsTheUpload` и мой собственный +`pgstore.TestTheBarIsOneMonotonicFractionThroughTheSigningStop`) — класс «правка съела чужой +комментарий» поймал меня дважды за один заход. Шесть номерных якорей в `bank.go` переведены на +символы и грепы; номерных якорей в файле теперь **ноль**, и почему по символам — вписано в сам +комментарий. -**5 (minor, обязательно сейчас) — миграция 00030 неверно цитировала D39.163.** Ссылка была на «словарь -стадий закрыт, третьего исключения нет»; D39.163 про то, что ВТОРОЕ исключение выдано под двумя -условиями. Переписано по существу: границу канона (§Boundaries) непрозрачному счётчику поколения -исключение не нужно вовсе, потому он им и является. Хеш в `migrations.sha256` пере-записан. - -**6 (minor) — четыре украденных док-комментария.** Все четыре возвращены своим функциям: -`ingest.Manifest.Whole()`, `pgstore.TestAClaimThatLostARaceToAReleaseIsRetriedAndNotAnError`, -`books.TestAManifestThatContradictsItselfNeverCostsTheUpload` и (мой собственный, п.4) -`pgstore.TestTheBarIsOneMonotonicFractionThroughTheSigningStop`. - -**7 (minor) — шесть номерных якорей в `bank.go`.** Переведены на символы и грепы (`writeCtx`, -`settleCtx`, `runner.BankApply`, греп `bank_moved_at =`, `if book.BankMoved`, `pgstore.StartRun`); -номерных якорей в файле теперь **ноль**. В сам комментарий вписано, почему по символам. +**5 (minor, обязательно сейчас) — миграция 00030 неверно цитировала D39.163.** Ссылка была на +«словарь стадий закрыт, третьего исключения нет»; D39.163 про то, что ВТОРОЕ исключение выдано под +двумя условиями. Переписано по существу: границу канона (§Boundaries) непрозрачному счётчику +поколения исключение не нужно вовсе, потому он им и является. Хеш в `migrations.sha256` пере-записан. **8 (minor, три места).** `readmodel.go` «small and bounded» ушло вместе со старым комментарием при п.1. `platform/README.md`: добавлен СЕДЬМОЙ канал (`tmctl bank-apply` — единственный, которым зона @@ -173,15 +181,12 @@ reads as coverage» — и сам её не исполняет до конца: ### Числа дофиксов (каждое — командой) - `make check` при трёх гейтах + условии хоста: **18 пакетов, EXIT=0, скипов 0**, линтер «0 issues», - `sqlc diff` чист. -- `counts.py --check`: битая форма пусто, хвост вне словаря пусто; открытых **97** (major 2 · minor 32 · - info 63). -- `counts.py --lint`: **0 проблемных якорей в 101 живом доке.** ⚠ По дороге пришлось пере-нацелить ещё - ДВА — их убила моя же правка `deploy/README.md` из п.8; правило «якорь, убитый твоим переездом, - чинишь ты» сработало на мне в тот же заход. -- Новых посадок: **M20**, **M21** (обе на правило п.1), обе красные адресно. -- Новый бенчмарк `BenchmarkChapterPage`: 0.617 мс против 0.496 мс до джойна. -- Тестов после дофиксов: **+12 новых, 3 пере-подписаны** (дифф `^func Test` исполнением + новые файлы). + `sqlc diff` чист; `counts.py --check` и `--lint` чисты. +- Новый бенчмарк `BenchmarkChapterPage`: **0.617 мс против 0.496 мс** до джойна (страница 100 глав, + вакуумированный корпус) — цена одного правила вместо двух. +- ⚠ Урок, стоивший времени: пере-нацелить пришлось ещё ДВА якоря, убитых моей же правкой + `deploy/README.md` из п.8 — правило «якорь, убитый твоим переездом, чинишь ты» сработало на мне + в тот же заход. ## ПАК P12 ОТРАБОТАН — семь major разобраны, обход `--verify-bank` снят и пробит живьём, канон 0.9.0; адверсариальный проход нашёл в СВОЕЙ работе три регрессии и они откачены (сессия `textmachine-main-b5`, 31.08) @@ -189,27 +194,15 @@ reads as coverage» — и сам её не исполняет до конца: правки — `docs/PROGRESS.md`, `docs/architecture/05-decisions-log.md` (D39.178), `05-decisions-index.md`; я их не трогал и в свой состав не включаю. -### Таблица комплектности против §3 (пункт → сделано → чем ДОКАЗАНО) +### Что взято НЕ целиком — только эти четыре пункта -| § | Взято? | Чем доказано | -|---|---|---| -| 3.1 `PD-425` | **да** | детач РОВНО записи (`WithoutCancel`+`recordBudget`); пин `TestTheBankMoveFactSurvivesAClientThatHungUp`, посадка M1 красная адресно | -| 3.2 `PD-403` | **половина** | счёт книги — да (эпоха, миграция 00030, канон 0.9.0); полоса прогона — НЕТ, откачено, новая строка `PD-435` | -| 3.2 `PD-404` | **да** | тот же носитель; пин `TestAddingTheEditorMovesTheEpochWithTheCountItRecomputes`, посадка M9 | -| 3.2 `PD-405` | **да** | отказ старта до дерева, ДО холда; пины ×2, посадки M10/M19 | -| 3.2 `PD-411` | **да** | миграция 00031, оба писателя сняты; вскрытый гап заведён строкой `PD-434` | -| 3.3 `PD-402` | **да** | константа `newestRun` в 10 склейках + `LatestRun`; 3 пина по местам, посадки M6/M7 | -| 3.4 `PD-424` | **половина** | счёт неудач и видимость — да (пин + M13); терминальная РУЧКА — нет, диспозиция подписана | -| 3.4 `PD-418` | **дешёвая половина** | оговорка в рантбуке; долговечное лечение — диспозицией, состояние по-прежнему недостижимо | -| 3.5 `PD-369` | **да** | `ErrKeyContended` оборачивает `ErrKeyInFlight`; серия **80/80 PASS, 0 FAIL, 0 SKIP** | -| 3.5 `PD-420` | **пере-замерена** | 3 параллельные полные батареи в чистых копиях: 18/18 каждая, 0 красных. Одна точка, не опровержение — строка открыта | -| 3.6 `PD-431` | **да** | сквозной пин через `auth.Authenticator` с ЖИВЫМ стором; посадка M12 — ИМЕННО та, которой строка заведена | -| 3.6 `PD-432` | **да** | рецепт дописан; опровергнутая команда `/proc/self/cgroup` снята (моя точка — третья) | -| 3.7 обход | **да, весь состав (а)+(б)+(в)** | миграция 00029; **живой пробой на стенде**, см. ниже | -| 3.7 строка 240 | **да** | и потом ПЕРЕ-починена: адверсариальный проход нашёл, что гард читал устаревший снапшот | -| 3.8 `PD-367` | **да** | симметричный пол; фикстуры расширены (`wholeManifest`), не обойдены; посадка M15 | -| 3.8 `PD-213` | **да** | форменный гейт версии манифеста; посадка M14 | -| 3.9 гигиена | **да** | 13 строк закрыто, 15 перенесено в секцию эры P12, 4 новые строки, 4 пере-диспозиции | +Комплектность против §3 промта (17 пунктов, промт архивирован) свелась к четырём неполным, и все +четыре несут носителя: `PD-403` — счёт книги да, полоса прогона откачена и заведена `PD-435` · +`PD-424` — счёт неудач и видимость да, терминальная РУЧКА нет (диспозиция подписана) · `PD-418` — +оговорка в рантбуке, долговечное лечение диспозицией · `PD-420` — пере-замерена тремя чистыми +параллельными батареями (18/18, 0 красных), это ОДНА точка, не опровержение, строка открыта. +Остальное закрыто и запинено; какая посадка какой пин ловит — комментарии `Mutation caught:` в +самих тестах. ### Живой пробой §3.7 — сделан НАСТОЯЩИЙ, и вот чем он отличается от достижимой половины @@ -276,15 +269,11 @@ reads as coverage» — и сам её не исполняет до конца: опровергнутой команде: у моей оболочки `cut -d: -f3 /proc/self/cgroup` = `/`, и это ТРЕТЬЯ точка против неё. - ⚠ **`sqlc` на машине НЕ БЫЛО**, а `make check` держит `sqlc diff` пререквизитом — то есть батарея зоны была недостижима, пока я не поставил пин `v1.31.1`. Стоит знать при следующем подъёме окружения. -- Три параллельные полные батареи в чистых копиях: 18/18 каждая, 0 красных (замер для `PD-420`). - `PD-369`, серия: `-count=80` ⇒ **80 PASS / 0 FAIL / 0 SKIP** (счёт по `^--- PASS`, не по коду выхода). -- Тесты: **+12 новых, 3 пере-подписаны** (дифф `^func Test` исполнением + новые файлы). - Мутационных посадок: **19**, каждая в СВОЕЙ копии дерева ВМЕСТЕ с каноном, вердикт по ДЕЛЬТЕ против чистой базы ТОЙ ЖЕ копии. Все 19 красные адресно. - Миграции: 00029 (снос `bank_released`), 00030 (эпоха), 00031 (снос четырёх мёртвых колонок). Down-путь гоняется существующим `TestARollbackSurvivesTheDataTheNewVocabulariesWrote` (DownTo(5)+Up) — зелен. -- Регистр: 435 строк, открытых **97** (major 2, minor 32, info 63) против 107 на входе; `counts.py --check` - чист по форме и словарю. ### Пере-подписанные пины — поимённо (D39.121: заказанная смена контракта, не подгонка) @@ -333,19 +322,12 @@ reads as coverage» — и сам её не исполняет до конца: ### Пинг аудита доков (три места зоны) — ИСПОЛНЕН тем же деревом -1. `platform/README.md`: «каналов ПЯТЬ» → **ШЕСТЬ**, шестой назван (`tmctl build` + сайдкар - `.book.`, пути в `StatusArtifacts.book_files`) вместе с правилом, что дверь выдачи обязана - СТРОИТЬ, а не подбирать лежащий рядом файл. ⚠ Счёт в этой фразе отставал от списка ДВАЖДЫ, поэтому в неё - вписано, что список считается, а не запоминается. -2. `platform/docs/STACK_DECISIONS.md`, «Инвентарь каналов движка»: строка `tmctl build` добавлена с - атомарностью, отсутствующим читателем, exit **16** `book_incomplete` и ловушкой exit 11 у интейка. -3. `platform/BACKLOG.md` П-12 **и** `PD-175`: продуктовая половина снята целиком (D39.176 п.1), осталась - инженерная гигиена без чьего-либо слова. Записано в ОБА носителя, а не только пингом — пинг уезжает в архив. - -⚠ **Плюс попутное из того же пинга: 13 битых якорей регистра пере-нацелены** (не пять — гейт `counts.py --lint` -на моём дереве называл тринадцать; часть убил мой же переезд, часть жила раньше). Все тринадцать наведены -ПО ТОКЕНУ, а не по номеру строки; двусмысленный (`case overran:` — два кандидата) разрешён вручную в пользу -`settleOne`, о котором строка. `--lint`: **0 проблемных якорей в 101 живом доке.** +Все три правки уехали в долговечные носители, а не остались пингом: счёт каналов движка в +`platform/README.md` (⚠ он ОТСТАВАЛ от списка дважды, поэтому во фразу вписано, что список +считается, а не запоминается) · строка `tmctl build` в «Инвентаре каналов движка» · снятие +продуктовой половины `П-12`/`PD-175` по D39.176 п.1 — в ОБА носителя. +⚠ Попутно пере-нацелены **13** битых якорей регистра (не пять, как называл пинг): наведены ПО +ТОКЕНУ, двусмысленный (`case overran:` — два кандидата) разрешён вручную в пользу `settleOne`. ### Вопросы оркестратору @@ -354,8 +336,8 @@ reads as coverage» — и сам её не исполняет до конца: продолжение §3.2, не его хвост. 2. **`PD-434`** (ресинк не чинит полосу): лечить его — значит дать ресинку писать в `chapters` и разобрать, как он не спорит с потоком, который те же строки пишет из `unit_resolutions`. Тоже отдельно. -3. Канон 0.9.0 у меня в дереве; баннер-компаньон `14-api-contract/README.md` по-прежнему говорит «по 0.8.0 - включительно» — по его же правилу это ТВОЙ акт лендинга, я не трогал. +3. ⚠ **СНЯТ:** баннер-компаньон `14-api-contract/README.md` отставал на 0.8.0 — оркестратор довёл его + до 0.9.0 актом лендинга (греп `по 0.9.0 включительно`). ## ДОФИКС после лендинга `63fcee5`: три строки регистра + §3.8-свип (сессия `textmachine-1b`, 29.08) @@ -410,13 +392,15 @@ cgroup. Сам `tm-runs.slice` лежит глубже, чем его ищут: ### Вопросы оркестратору (не блокируют работу) 1. **Три живых оператора без единого исполнения — это находки регистра, независимо от sqlc.** Завожу - строками или оставляю в отчёте? + строками или оставляю в отчёте? ⚠ Один из трёх с тех пор заведён — `PD-433` (мёртвая ветвь + `errIdentityRace`), и она открыта. 2. **M6 — не гипотеза, а дефект с прод-последствием** (окно бездействия перестаёт скользить). Он существует СЕГОДНЯ как незапиненное свойство. sqlc закроет перестановку, но недостающее утверждение в тесте — отдельная работа, и параметрическую половину (`sessions.go:53`) он не - закроет вовсе. Чинить в этом паке или строкой регистра? -3. `BACKLOG.md` П-19 говорит «41 запрос» и `sessions.go`(5) — устарело на `StillLive` (пак P11, - лендинг `e548e5a`). Правка бэклога — твоя зона, не моя. + закроет вовсе. Чинить в этом паке или строкой регистра? ⚠ ОТВЕЧЕНО делом: заведена `PD-431`, + закрыта паком P12 сквозным пином через `auth.Authenticator` с живым стором. +3. ⚠ **ИСПОЛНЕНО:** `BACKLOG.md` П-19 приведён к дереву — живая граница набора 42 места / 41 текст / + 40 конвертируемых, и строка помечена ИСПОЛНЕННОЙ (D39.172). ## ИТОГ пака `sqlc` (П-19): построено, 40 запросов из 42, три поправки к собственной записке (сессия `textmachine-1b`, 29.08) @@ -486,15 +470,6 @@ read-модель дотягивается: конвертация меняет берёт чужую `tx` и зовёт `New(tx)`, а сам оператор уехал в `platform/internal/pgstore/queries/credits.sql:115`=`select id from books where id = sqlc.arg(id) for update;`. -### Развилки §3.4 — обе решены прогоном - -- **Каталог тот же.** С генерённым файлом гейт напечатал 174 против 173 и СПЛАНИРОВАЛ генерённый - оператор против живой схемы (`-- name:`-комментарий планированию не мешает). Охват не расширялся, пол - `140` не двигался — генерённые операторы попадают под гейт даром, потому что sqlc кладёт SQL в - package-level `const`, а экстрактор сворачивает именно такие. -- **Второго носителя схемы нет.** `schema:` указывает на тот же `internal/pgstore/migrations/`, который - гейтит `migrations.sha256`; sqlc читает goose-формат как есть. - ### Деньги Подстановочный override `column: "*.*_micro_usd"` → `money.MicroUSD` покрывает все девять денежных @@ -507,15 +482,13 @@ read-модель дотягивается: конвертация меняет кастом это `int64`. Поэтому касты стоят, а конверсия названа в комментарии на каждом месте. Override с явным именем алиаса тоже НЕ применяется — проверено. -### Доказательства (каждое — прогон, не чтение) +### Доказательства — прогоном, не чтением -| Проверка | Команда | Итог | -|---|---|---| -| Батарея | `make check` (`-race`, все 4 гейта стенда) | **18 пакетов, EXIT=0, скипов 0, линтер 0 issues, `sqlc diff` чист** | -| Уязвимости | `make vuln` | `No vulnerabilities found`; `go.mod`/`go.sum` НЕ изменились — sqlc это тул, не зависимость | -| Ни один тест не исчез | `git grep -hoE '^func (Test\|Fuzz\|Benchmark)\w*' HEAD` против дерева | **638 → 643** против HEAD `72434ce`; дифф — РОВНО пять моих добавлений, строк `<` нет вовсе: ни потерь, ни переименований | -| Гейт актуальности РАБОТАЕТ | правка `queries/credits.sql` без регенерации | `sqlc diff` exit **2**; восстановление — exit 0 | -| Якоря реестра | `python3 docs/scripts/counts.py --lint` | **0 проблемных**: четыре якоря, сдвинутые моими правками, пере-нацелены — `PD-377`/`PD-397`/`PD-394` сдвигом на ±1–2 строки, а `PD-380` ПЕРЕ-НАЦЕЛЕН, а не сдвинут (токен `s.pool.Exec` исчез вовсе, SQL уехал в `queries/sessions.sql`; новая цель — строка, где `maxAge` СТАНОВИТСЯ значением, причина записана в самой строке реестра) | +Батарея при всех четырёх гейтах стенда: 18 пакетов, EXIT=0, скипов 0, линтер 0 issues, `sqlc diff` +чист · `make vuln` чист, и `go.mod`/`go.sum` НЕ изменились — sqlc это тул, а не зависимость · +перепись `^func (Test|Fuzz|Benchmark)` против HEAD `72434ce`: **638 → 643**, строк `<` нет вовсе, +то есть ни один тест не исчез и не переименован · гейт актуальности проверен нарушением: правка +`queries/credits.sql` без регенерации даёт `sqlc diff` exit **2**. ### Что пак КУПИЛ — посадки на конвертированном дереве @@ -541,12 +514,6 @@ Override с явным именем алиаса тоже НЕ применяе собственный тест.** Починено строкой, вставленной прямо в SQL с РАЗНЫМИ значениями (700000/900000); после этого посадка красная с точным диагнозом. Класс D39.171, найден только исполнением. -### Новые тесты (закрывают то, что пак ИЗМЕРИЛ как непроверенное) - -`TestAnOpenHoldIsListedWithItsOwnAmountAndBook` · `TestAnAccountWithNoLedgerRowsHasNoCreditRatherThanAnError` · -`TestUserByIdentityResolvesAPairAndInventsNoAccount` · `TestTheLoginJournalRetentionDeletesOnlyWhatIsOlderThanTheCutoff`. -Каждый пере-проверен посадкой — все четыре краснеют адресно. - ### Адверсариальный проход по готовой работе (обязателен, слово владельца) — семь находок, все починены Отдельный агент по шести осям, на готовом дереве, с правом мутировать копии. **Саму конверсию сломать @@ -587,19 +554,12 @@ Override с явным именем алиаса тоже НЕ применяе фиксированному расписанию). Комментарий при этом утверждал, что класс закрыт. **Утверждение в комментарии — это тоже заявление, и оно требует той же проверки исполнением, что и число в отчёте.** -### Вопросы оркестратору +### Вопросы оркестратору — все три ЗАКРЫТЫ позже -1. **`Touch` (`sessions.go:53`) — дефект БЕЗ строки регистра, и пак его не закрывает.** Сделать - `:execrows` с проверкой было бы изменением ПОВЕДЕНИЯ (сейчас ноль строк — норма), а не конверсией, - поэтому молча не взяла. Заводить строку или чинить отдельным паком? -2. **`observe.go` остался вне набора** — это уменьшает обещанный П-19 выигрыш на худшем случае набора. - Правка бэклога твоя: П-19 говорит «41 запрос» и `sessions.go`(5), фактически 42 места / 41 текст / - 40 конвертируемых. -3. Ещё две дыры того же класса, найденные агентом и НЕ закрытые паком: `identity.go` `on conflict - (provider, subject) do nothing` — ветка `errIdentityRace` мертва целиком, то есть ON CONFLICT не - проверяет никто; и `PD-423` **не воспроизвёлся** — `cut -d: -f3 /proc/self/cgroup` даёт `/init.scope`, - а `TestARunIsBoundedByItsOwnCgroup` зелёный в трёх прогонах подряд у меня и у агента. Симптом строки - стоит пере-проверить. +`Touch` без проверки `RowsAffected` заведён `PD-431` и закрыт паком P12 сквозным пином через +`auth.Authenticator` · `observe.go` вне набора записан в `BACKLOG.md` П-19 живой границей (42 места / +41 текст / 40 конвертируемых) · мёртвая ветвь `errIdentityRace` в `identity.go` несёт открытую строку +`PD-433`, а `PD-423` получил третью точку от P12 и остаётся открытым без установленного диагноза. ## ПОСЛЕ ЛЕНДИНГА (2): аудит документации зоны — семь позиций разобраны, комментарий шва исправлен (сессия `textmachine-c0`, 29.08) @@ -612,29 +572,24 @@ Override с явным именем алиаса тоже НЕ применяе берётся СЕГОДНЯ — гейт проводки вместе с `--max-units`. Вторая половина важнее первой, и довод его: неверный ДОВОД сессия перепроверит, неверное ОЖИДАНИЕ она примет как карту. -**⛔ Что было ложью в доках зоны:** -1. `README.md` объявлял, что «ручки поправить/добавить термин нет ни в зоне, ни в каноне» — дверь - `POST /books/{bookId}/bank/corrections` построена паком P9 и смонтирована. Предложение склеивало - снятую ПЕР-ТЕРМНУЮ ПОДПИСЬ с правкой термина. Исправлено. -2. Там же «Три канала движка, и других нет» — и тут же перечислялись ПЯТЬ. Счёт правился, слово нет. -3. **Число условий батареи жило в ТРЁХ файлах и они расходились** (README — два, `ENGINEERING_STANDARDS` — три, - `STACK_DECISIONS` — четыре). Это самый дешёвый способ получить ложную приёмку: сессия, честно - исполнившая §3.1 по устаревшей копии, объявит «скипов ноль» при красном тесте. **Носитель теперь - ОДИН** — `STACK_DECISIONS`, «Гейты батареи»; две другие копии заменены ссылкой на него. -4. `STACK_DECISIONS` в лечебной команде самой частой грабли стенда указывал на несуществующий срез - `tm.slice` (в коде `runner.go` — `tm-runs.slice`), причём двумя абзацами выше тот же файл писал - правильное имя. Оператор получал «No such file or directory». -5. `deploy/README.md` держал жёсткое правило «эмиттер не выкатывается, пока `tmctl migrate` не - заленден». Глагол существует и заленджен (`backend/cmd/tmctl/migrate.go`, свой код выхода 13). - Правило блокировало выкат по несуществующей причине — худший род блокировки, снаружи неотличимый - от осторожности. +**⛔ Что было ложью в доках зоны — семь мест, все исправлены в самих доках.** Пять из семи — +устаревшее отрицание при построенном механизме (`README.md`: «ручки правки термина нет» при +смонтированной двери P9; «три канала движка, и других нет» при пяти в том же перечне; +`deploy/README.md`: запрет выката эмиттера «пока `tmctl migrate` не заленден» — глагол существует +и заленджен; `STACK_DECISIONS`: лечебная команда указывала на несуществующий срез `tm.slice` при +правильном `tm-runs.slice` двумя абзацами выше; `PLATFORM_DIRECTION`: таблица говорила «кодоген — +ВЗЯТЬ, доказано 05.08» при баннере «НЕ БРАТЬ» — замер доказал, что инструмент РАБОТАЕТ, а не что +его берут). Два оставшихся стоили бы дороже прочих и потому названы полностью: + +3. **Число условий батареи жило в ТРЁХ файлах и они расходились** (README — два, + `ENGINEERING_STANDARDS` — три, `STACK_DECISIONS` — четыре). Это самый дешёвый способ получить + ложную приёмку: сессия, честно исполнившая §3.1 по устаревшей копии, объявит «скипов ноль» при + красном тесте. **Носитель теперь ОДИН** — `STACK_DECISIONS`, «Гейты батареи»; две другие копии + заменены ссылкой на него. 6. **`deploy/README.md` показывал боевой env, при котором инстанс не запустит НИ ОДНОГО перевода:** нет `ENGINE_BIN`, `CTL_BIN`, `STATE_DIR` (дефолт вне `ReadWritePaths=` при `ProtectSystem=strict`) и `ENGINE_KEYS_PATH` — единственного канала ключей. Каждый ОПЛАЧЕННЫЙ прогон падал бы `exit 10`, а на буте это WARN, то есть тихо. Дописаны все четыре, с объяснением, почему каждая обязательна. -7. `PLATFORM_DIRECTION.md` нёс в таблице «кодоген — **ВЗЯТЬ**, доказано 05.08» при баннере того же - файла «решение P7 по обоим — НЕ БРАТЬ». Замер 05.08 верен и остаётся: он доказал, что инструмент - РАБОТАЕТ, а не что его берут. Слово вердикта было прочитано из замера. **⛔ И самое неприятное — в РЕГИСТРЕ, и половина этого моя.** Секция и статус разошлись у десяти строк: девять `fixed` и один `accepted-risk` лежали под заголовками «Открытые», то есть человек, @@ -696,46 +651,33 @@ $0.005460). Накладные масштабируются КНИГОЙ, а н ### Числа сдачи (§4.1) -⚠ **Два разных дерева, и я называю оба, потому что путать их — обычный способ соврать зеленью.** +Сдаваемое дерево (`ContractVersion` 0.8.0) при трёх гейтах: **17 пакетов ok, 838 PASS, скипов 0, +1 FAIL** — `TestARunIsBoundedByItsOwnCgroup`; на том же дереве до последних четырёх фиксов и на +базовой линии HEAD `fbe6cf3` — **18 пакетов, EXIT=0, скипов 0**. Линтер `0 issues`. -| дерево | команда | результат | -|---|---|---| -| **этот пак** (сдаваемое дерево, `ContractVersion` 0.8.0), три гейта | `go test ./... -race -count=1 -v` при `TM_PLATFORM_TEST_DSN` + `_ENGINE_BIN` + `_BOOK_TEMPLATE` | **17 пакетов ok, 838 PASS, скипов 0, 1 FAIL** — `TestARunIsBoundedByItsOwnCgroup` (`~/tm-p11/logs/FINAL2-verbose.log`; тот же результат и в `FINAL-verbose.log` до последних четырёх фиксов). ⚠ Не мой дифф и не флейк: см. ниже | -| тот же прогон на дереве в 02:04, до последних правок | та же команда | **18 пакетов, EXIT=0, скипов 0, 836 PASS** (`~/tm-p11/logs/final4-verbose.log`) | -| **этот пак**, линтер | `golangci-lint run --timeout=10m ./...` | `0 issues` (`~/tm-p11/logs/FINAL-lint.log`) | -| базовая линия HEAD `fbe6cf3` до правок, три гейта | та же команда | 18 пакетов, EXIT=0, скипов 0 (`~/tm-p11/logs/baseline2.log`) | -| **этот пак БЕЗ гейтов** | `env -u TM_PLATFORM_TEST_DSN -u …_ENGINE_BIN -u …_BOOK_TEMPLATE go test ./... -count=1 -v` | **EXIT=0 и 304 скипа** (`~/tm-p11/logs/final-nodsn.log`) | -| HEAD без гейтов | та же команда | EXIT=0 и **287** скипов (`~/tm-p11/logs/baseline-nodsn.log`) | +⛔ **ЕДИНСТВЕННЫЙ КРАСНЫЙ, и я НЕ выдаю его за зелёный.** Пакет `internal/runner` мой дифф не +касается вовсе, на том же коде тест был зелен в трёх предыдущих полных батареях и затем пять раз +подряд красен В ИЗОЛЯЦИИ — то есть не флейк и не нагрузка. Причина найдена ВНЕ Go и вне батареи: +`systemd-run --user --scope -p MemoryMax=64M …` на этом хосте даёт процессу спокойно занять 400 МиБ +и выйти с кодом 0. **Тест ПРАВ**: он ловит ровно то, ради чего написан, — что потолок памяти +прогона на этом хосте иллюзорен. Разбор, уточнение механизма приёмкой и ДВЕ последующие точки, +которые ему противоречат, — строкой `PD-423`, она единственный носитель этого сюжета. -⛔ **ЕДИНСТВЕННЫЙ КРАСНЫЙ, и я НЕ выдаю его за зелёный.** `internal/runner` -`TestARunIsBoundedByItsOwnCgroup` падает на финальном прогоне. Что установлено ИСПОЛНЕНИЕМ, а не -рассуждением: -- пакет `internal/runner` мой дифф не касается вовсе (`git diff --stat` по нему пуст); -- на ТОМ ЖЕ коде тест был зелен в трёх предыдущих полных батареях (`baseline2`, `final2`, `final4`) и - затем **пять раз подряд красен в изоляции** — то есть это не флейк и не нагрузка; -- причина найдена ВНЕ Go и вне батареи: `systemd-run --user --scope -p MemoryMax=64M …` на этом - хосте даёт процессу спокойно занять **400 МиБ** и выйти с кодом 0. Потолок памяти не применяется - самим systemd, `Delegate=no` и `MemoryMax=infinity` на `tm-runs.slice`. -- **Тест ПРАВ**, и его сообщение точное: он ловит ровно то, ради чего написан, — что потолок памяти - прогона на этом хосте иллюзорен. +⚠ **И моя собственная ошибка вывода, которую это вскрыло.** Я записала этот тест во «флейки под +параллельной нагрузкой» на основании СОВПАДЕНИЯ, а не замера; серийный прогон опроверг. `PD-420` +переписана только про `TestAClaimThatLostARaceToAReleaseIsRetried…`, а условие хоста вынесено +отдельной строкой `PD-423`. -⚠ **И моя собственная ошибка вывода, которую это вскрыло.** Ранее я записала этот тест во «флейки под -параллельной нагрузкой» — на основании того, что он краснел в чистых копиях кампании. Это было -выведено из СОВПАДЕНИЯ, а не измерено; серийный прогон опровергает. Строка регистра исправлена: -`PD-420` теперь только про `TestAClaimThatLostARaceToAReleaseIsRetried…` (он серийно зелен трижды -подряд — пере-проверено), а условие хоста вынесено отдельной строкой `PD-423`. Заодно это ЧЕТВЁРТОЕ -условие батареи, которого рецепт не называет. +⚠ **«Три гейта» здесь — это ТРИ ПЕРЕМЕННЫЕ** (`_TEST_DSN` · `_TEST_ENGINE_BIN` · +`_TEST_BOOK_TEMPLATE`), а `STACK_DECISIONS` считает гейтом УСЛОВИЕ и потому даёт свою тройку. +Числа сходятся, потому что третье условие на этом хосте выполнено, — но счёт разный, и путать их +не надо. -⚠ **«Три гейта» здесь — это ТРИ ПЕРЕМЕННЫЕ** (`_TEST_DSN` · `_TEST_ENGINE_BIN` · `_TEST_BOOK_TEMPLATE`), -а `STACK_DECISIONS` считает гейтом УСЛОВИЕ и потому даёт свою тройку: DSN · пара ENGINE_BIN+TEMPLATE · -достижимый пользовательский менеджер systemd. Числа сходятся, потому что третье условие на этом хосте -выполнено (`/run/user/1000` жив) — но счёт разный, и путать их не надо. - -Скипы считаны командой (`grep -cE '^\s*--- SKIP'`), а не глазами, и НА ОБОИХ деревьях отдельно: -первая редакция этой таблицы писала «287, любое из двух», а это неправда — пак добавил пины, -гейченные тем же DSN, так что его собственное число 304. Разница 17 — ровно те новые тесты, которые -без Postgres не идут. Зелень без переменных не значит ни одного из этих чисел. ⚠ И правило, которое я забрала у соседней сессии и считаю более -общим: **зелёная батарея — это ПОЛНЫЙ СПИСОК ПАКЕТОВ плюс отсутствие FAIL, а не отсутствие FAIL.** +⚠ Скипы считаны командой (`grep -cE '^\s*--- SKIP'`) и НА ОБОИХ деревьях отдельно: первая редакция +писала «287, любое из двух», а это неправда — пак добавил 17 пинов, гейченных тем же DSN, значит +его собственное число 304 (оба числа живут в `STACK_DECISIONS`, «Гейты батареи»). ⚠ И правило, +которое я забрала у соседней сессии и считаю более общим: **зелёная батарея — это ПОЛНЫЙ СПИСОК +ПАКЕТОВ плюс отсутствие FAIL, а не отсутствие FAIL.** ### Что доказано ЖИВЬЁМ @@ -794,18 +736,25 @@ $0.005460). Накладные масштабируются КНИГОЙ, а н Дословно — `~/tm-p11/probes/pd385-before.txt` и `pd385-after.txt`. -### Таблица комплектности против §3 (§6): строка → что сделано → каким ИСПОЛНЕНИЕМ подтверждено +### Семь строк заказа — что построено (доказательства ниже, по разделам) -| строка | что сделано | подтверждено ИСПОЛНЕНИЕМ | -|---|---|---| -| **`PD-379`** (vuln, major) | Личность и способность её пере-спросить — ОДНО значение: `auth.Principal` получил непубличное поле и метод `StillLive(ctx)`; `guard` его связывает; `pump` зовёт его ПЕРВЫМ ДЕЛОМ на каждой итерации и на отказ шлёт терминальный кадр `session_ended` с watermark соединения. Отдельный запрос стора `StillLive` БЕЗ клаузы idle. Окна нет — проверка на каждом тике | **ДВА раздельных живых сценария с сохранёнными транскриптами** (а) отзыв → поток кончился через 1 с, (б) короткий абсолютный потолок без отзыва → кончился ровно на потолке, пережив idle-окно · 9 юнит-пинов (`stream_session_test.go` 5, `sessions_test.go` 2, `principal_test.go` 2 — счёт командой `grep -c "func Test"`) · посадки — см. таблицу кампании ниже | -| **`PD-385`** (major) | Популяция «кончился, а деньги нет» вошла в `StalledRuns` (колонка `PHASE`), в гейдж `tm_platform_runs_stalled` и получила терминальную ручку `run abandon` (закрывает КАЖДЫЙ осиротевший холд, снимает отсрочку, штампует `settled_at`). Новые `AbandonVerdict`, `ErrMoneyAlreadyClosed`, `ErrSettlementNotStuck` | **Живое до/после на состоянии из штатных путей** (таблица выше) · 3 пина `internal/runs` + 3 пина CLI · посадки — см. таблицу кампании ниже | -| **`PD-384`** (minor) | Неудачей считается ВЕРДИКТ расчёта, а не только исчерпание бюджета: `settle` вернул три состояния, `settleOne` судит по ним. Первая неудача НЕ откладывается (сохраняет прежние 15 с пользователю), со второй — бэкофф, капнутый пятью минутами | Пины `TestTheFirstFailedSettlementIsRetriedAtOnce…`, `TestASettlementWithNoBaselineIsCountedRatherThanTreatedAsARace` · посадки — см. таблицу кампании ниже | -| **`PD-391`** (minor) | `AbandonRun` снимает `reconcile_after` В ОБЕИХ ветках | Пин `TestAbandoningAStalledRunGivesTheHoldBackOnTheNextSweepAndNotIn30Minutes` (растит 5 неудач штатными путями, потом сверяет холд ПОСЛЕ свипа на НЕДВИНУТЫХ часах) + `TestAbandoningAStuckSettlementClearsItsDeferralToo` · руководство `deploy/README.md` исправлено | -| **`PD-394`** (info) | Гард отрицательного расхода получил ИМЯ (`ErrNegativeSpend`) и пин | Посадка — см. таблицу кампании ниже. ⚠ Первая редакция пина мутацию НЕ ловила — ловил констрейнт схемы, ровно та транзитивность, о которой строка и говорит; пин переписан на `errors.Is` | -| **`PD-397`** (info) | Триггер ОТКЛОНЁН с разбором (сработал бы на каскадном удалении пользователя, которое миграция объявляет границей, и сломал бы законную фикстуру). Вместо него: проза сделана честной («держит КОД, а не схема») + гейт по SQL пакета, что ни один `update`/`delete` по `credit_ledger` не написан | Гейт `TestTheLedgerIsAppendOnlyInTheCodeThatWritesIt` (173 statements — число растёт с каждым новым SQL пакета; сверено по `FINAL2-verbose.log`) · посадка — см. таблицу кампании ниже | -| **`PD-376`** (minor, деньги) | Взят готовый пин пака `P8-REVIEW` — вариант `r1_` как более сильный (ходит настоящими дверями, наименьший в СЕРЕДИНЕ) — и усилен второй книгой, чтобы исполнялся и фильтр `r.book_id` | **Посадка `min`→`max` ПОЙМАНА**: чистая копия EXIT=0 → с мутацией EXIT=1, единственный красный тест — этот пин (`~/tm-p11/mut/m376_max/verdict.txt`). ⚠ Посадка ставилась на РАННЮЮ редакцию пина, без второй книги: то есть исполнение `min` как ВЫБОРА доказано, а исполнение фильтра `r.book_id` — нет, и это подписано пропуском в таблице кампании | -| **сверх пака** | `Settle` перестал выбрасывать флаг `applied` своей леджер-записи (`ErrSettlementKeySpent`) | Пин `TestASettlementWhoseKeyWasSpentIsRefusedRatherThanSilent` · посадка — см. таблицу кампании ниже. Достижимость сегодня НУЛЕВАЯ — второй холд на ту же попытку отказан; класс тот же, что у `PD-394` | +- **`PD-379`** (vuln, major): личность и способность её пере-спросить стали ОДНИМ значением — + `auth.Principal` получил `StillLive(ctx)`, `pump` зовёт его ПЕРВЫМ ДЕЛОМ на каждом тике и на отказ + шлёт терминальный кадр `session_ended` с watermark соединения. Запрос стора `StillLive` — БЕЗ + клаузы idle (поток не может сдвинуть собственное окно бездействия). Окна нет: проверка на каждом тике. +- **`PD-385`** (major): популяция «прогон кончился, а деньги нет» вошла в `StalledRuns` (колонка + `PHASE`), в гейдж `tm_platform_runs_stalled` и получила терминальную ручку `run abandon`. +- **`PD-384`** (minor): неудачей расчёта считается ВЕРДИКТ, а не только исчерпание бюджета; первая + неудача не откладывается (прежние 15 с пользователю), бэкофф со второй и капнут пятью минутами. +- **`PD-391`** (minor): `AbandonRun` снимает `reconcile_after` В ОБЕИХ ветках. +- **`PD-394`** (info): гард отрицательного расхода получил ИМЯ (`ErrNegativeSpend`) и пин. +- **`PD-397`** (info): триггер append-only ОТКЛОНЁН — довод в §9 ниже; вместо него гейт по SQL пакета. +- **`PD-376`** (minor, деньги): взят и усилен второй книгой пин пака `P8-REVIEW` (вариант `r1_`). +- **сверх пака:** `Settle` перестал выбрасывать флаг `applied` своей леджер-записи + (`ErrSettlementKeySpent`); достижимость сегодня НУЛЕВАЯ — второй холд на ту же попытку отказан. + +Чем каждое доказано — разделы «Что доказано ЖИВЬЁМ» и «Посадки мутаций» ниже; какая посадка какой пин +обязана валить — комментарии `Mutation caught:` в самих тестах. ### ⚠ ПРАВКИ ЧУЖИХ, ДО-ПАКОВЫХ ТЕСТОВ — три штуки, названы поимённо (D39.121) @@ -959,23 +908,9 @@ HTTP-старт → настоящий спавн → настоящий вых которая не компилируется, — не поведенческая мутация, и её вердикт пуст). Дерево на время кампании заморожено. -| посадка | что ломает | ДЕЛЬТА (красное только под мутацией) | -|---|---|---| -| `r_nocheck` | `pump` перестаёт пере-спрашивать сессию | `TestARevokedSessionEndsAStreamThatIsAlreadyRunning`, `TestARevokedCallerIsGivenNoFurtherFrames` | -| `r_idle` | вернуть клаузу `idle_expires_at` в `StillLive` | `TestStillLiveAnswersRevocationAndTheCeilingButNotTheIdleWindow` (+ подслучай про idle), `TestTheStreamsQuestionIsNotTheDoorsQuestion` | -| `r_head` | штамповать голову истории вместо watermark соединения | `TestARevokedSessionEndsAStreamThatIsAlreadyRunning` | -| `r_open` | нулевой `Principal` отвечает «жив» | `TestAPrincipalNobodyAuthenticatedIsNotLive` (три подслучая), `TestAPrincipalWithNoSessionBehindItIsNotLive` | -| `r_wirename` | переименовать ЗНАЧЕНИЕ константы кадра (`session_ended` → `end`) | `TestARevokedSessionEndsAStreamThatIsAlreadyRunning` | -| `r_listnarrow` | сузить settling-ветвь `StalledRuns` обратно к живым попыткам | `TestTheRunsListingShowsAStuckSettlementAndNamesThePhase`, `TestARunWhoseSettlementIsStuckReachesTheOperatorsSurfaces`, `TestASettlementInFlightIsNotInTheOperatorsTable`, `TestARunThatKeepsFailingBecomesTheOperatorsProblem` | -| `r_gaugenarrow` | то же в гейдже `tm_platform_runs_stalled` | `TestARunWhoseSettlementIsStuckReachesTheOperatorsSurfaces`, `TestARunThatKeepsFailingBecomesTheOperatorsProblem` | -| `r_gate` | снять допуск по порогу у `abandonSettlement` | `TestASettlementThatHasNotFailedIsRefusedRatherThanGivenAway` | -| `r_oneorphan` | закрывать только ПЕРВЫЙ осиротевший холд | `TestAbandoningASettlementClosesEveryOrphanedHoldOfTheRun` | -| `r_defer391` | не снимать `reconcile_after` в живой ветке `AbandonRun` | `TestAbandoningAStalledRunGivesTheHoldBackOnTheNextSweepAndNotIn30Minutes` | -| `r_cheap384` | дешёвый провал расчёта снова не считается | пять тестов, включая `TestASettlementWithNoBaselineIsCountedRatherThanTreatedAsARace` и `TestAnOperatorCanEndAStuckSettlementAndTheMoneyComesBack` | -| `r_firstfast` | убрать быстрый первый ретрай расчёта | пять тестов, из них **три ДО-ПАКОВЫХ** (`TestADeferredSettlementIsNotChargedForTheNextRunOfTheSameBook`, `TestAFailedStatusCallLeavesTheMoneyExactlyWhereItWas`, `TestASettlementThatCannotBeReadLeavesTheHoldOpen`) | -| `r_negative` | снять гард отрицательного расхода | `TestSettlementRefusesANegativeSpendBeforeItReachesTheLedger` | -| `r_applied` | выбросить флаг `applied` в `Settle` | `TestASettlementWhoseKeyWasSpentIsRefusedRatherThanSilent` | -| `r_ledgeredit` | настоящий `tx.Exec` с `update credit_ledger` внутри `appendLedger` | `TestTheLedgerIsAppendOnlyInTheCodeThatWritesIt` | +Состав кампании поимённо здесь не дублируется: каждая посадка названа в комментарии `Mutation +caught:` того теста, который она обязана валить (греп по `internal/` и `cmd/`). Пятнадцать посадок, +пятнадцать поимённых красных, ни одного нетопичного. ⚠ **`r_firstfast` — самая красноречивая из пятнадцати.** Она валит ТРИ теста, написанных до этого пака, и это лучший доступный аргумент, что быстрый первый ретрай не выдумка, а восстановление @@ -1105,21 +1040,13 @@ HTTP-старт → настоящий спавн → настоящий вых ### Пины и посадки пересбора -Переписаны/добавлены: `TestACorrectionRecordsTheBankMoveAndAPreviewDoesNot` (+ветка already_applied -пере-штампует после ClearBankMove) · `TestAStartOverAMovedBankCarriesBothConsentsToTheSpawn` -(`--accept-rebill=0.060000` = холд 2 глав) · `TestAFailedRunKeepsTheFactAndAReadyRunRetiresIt` -(жизненный цикл факта через настоящий Sweep-путь) · `TestTheRePassDoorAdmitsOnAMovedBankAndRefusesWithoutOne` -(холд −150000 = вся 5-главная книга) · `TestAResumeOverAMovedBankGrantsTheConsents` (90000 = бюджет -строки) · `TestARePassIsBoughtAgainNotResumed` · `TestARePassRunsBarIsOneUnitOfWork` (0/1 → 1/1; -C=0 без консента отвергнут). Посадки: M-B2 (критерий квитанции мёртв) · M-C2 (гасит любой исход) · -M-G (консент не фондирован) · M-H (ветка полосы снята → 0/0 пойман) — **4/4 топично**. +Семь пинов переписаны/добавлены под новую форму (греп `TestARePass` и `TestAStartOverAMovedBank` в +`internal/runs`), посадки — **4/4 топично**: критерий квитанции мёртв · факт гасится любым исходом · +консент не фондирован · снята ветка полосы (0/0 пойман). Что каждая обязана валить — комментарии +`Mutation caught:` в самих пинах. -### Числа пересбора (командой) - -`go test ./... -race -count=1 -v` с гейтами → **EXIT=0, 18 пакетов ok, SKIP=0, RUN=803**, -FAIL/DATA RACE — 0; `golangci-lint` → **0 issues**; -`gofmt -l` пусто. Опись: `git status --short -- platform/` — дифф СЖАЛСЯ против сданного -(упразднений больше, чем добавлений). +Батарея пересбора: `go test ./... -race` с гейтами → **EXIT=0, 18 пакетов, SKIP=0**, FAIL/DATA RACE — +0, линтер 0 issues. Опись: дифф СЖАЛСЯ против сданного — упразднений больше, чем добавлений. ### Дописка: провод 0.7.0 смонтирован (четыре пункта приёмки) @@ -1135,8 +1062,6 @@ FAIL/DATA RACE — 0; `golangci-lint` → **0 issues**; в шапке `StatusReport` осталось ИМЕНОВАННОЕ объяснение, почему пара не берётся (тайминг свёртки) и с чем вернётся (движковый глагол сметы). -Батарея после провода (финальная этого раунда): `go test ./... -race -count=1 -v` с гейтами → -**EXIT=0, 18 пакетов ok, SKIP=0, RUN=803**, FAIL/DATA RACE — 0; линт **0 issues**; `gofmt -l` пусто. ### Остатки, названные честно @@ -1194,23 +1119,32 @@ FAIL/DATA RACE — 0; `golangci-lint` → **0 issues**; (0.6.0). Всё исполнено, кроме ДВУХ пунктов с несогласием (аргументы ниже — обе позиции по норме «несогласие говори»). -### Фиксы в дереве (сверх сданного пака; каждый с пином, посадки — в копии `~/tm-p9-mut2`) +### Фиксы в дереве (сверх сданного пака) — F1…F13 -| # | Что | Пин | Посадка | -|---|---|---|---| -| 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`) снова зелёный | — | +Каждый несёт пин; какая посадка какой пин обязана валить — комментарии `Mutation caught:` в самих +тестах, поэтому колонки «Пин»/«Посадка» здесь не дублируются. + +| # | Что | +|---|---| +| F1 | **MAJOR(а) 1 МиБ**: `ingest.EncodeDecisions` рендерит с `SetEscapeHTML(false)` (документ читает движок, не браузер; выбор обоснован в комментарии) **+ жёсткий гейт**: рендер сверяется с зеркалом движкового капа `ingest.MaxDecisionsDocument` ДО спавна → `ErrBankDocumentTooLarge` → **413** (остаточная конвертная полоса ~40 байт закрыта гейтом, слово всегда канонное) | +| F2 | **MAJOR(б) мьютекс**: `lockBook(ctx)` — одноместный канал вместо `sync.Mutex`, ожидание наблюдает контекст; **бюджет двери ставится ДО очереди** (накрывает ожидание+вызов); Start/Resume передают свои ctx; отмена в очереди корректно декрементит refcount | +| F3 | **Р1 окно стопа**: `ReadBookForRun` вырос полем `LiveRunAwaitingBank` (живая строка в `awaiting_bank`); предикат двери — `HasLiveRun && !LiveRunAwaitingBank`; Start по-прежнему держится на `HasLiveRun` (вторая строка сломала бы `runs_one_live_per_book`); худший случай окна — движок ещё дожёвывает → честный класс 12 → 503 «повтори». Довод в комментарии переписан, противоречие с `reconcile.go:1122` разрешено в пользу кода | +| 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) считают КЛЮЧИ | +| 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), файлы вне маски не трогаются | +| F10 | **Д5**: комментарий `spawn.go` о сбросе `--verify-bank` переписан с упразднённого D39.144-контракта на настоящее основание (память v16 покрывает карту; старое основание питало дырявый гард — названо в комментарии) | +| F11 | **Н5 hello**: при `resuming` hello несёт `last` клиента, не голову истории (свежий коннект — голову, как и `from` четырьмя строками ниже) | +| F12 | **Н1-гард**: `Resume` под мьютексом сверяется с новым `pgstore.LatestRunID` (тот же порядок, что `lastRun`) — не-последний прогон получает `ErrNotResumable` с человеческим доводом; реконсилерский RestartRun в гарде не нуждается (живой прогон всегда последний по `started_at`: пока он жив, новый не стартует) | +| F13 | **Канон 0.6.0** (третье сообщение): `ContractVersion` → 0.6.0; комментарий `wireProgress` переписан (сквозная доля и `stage` теперь КАНОННЫ, открытый словарь; оба условия исключения шапки — вывод платформой из тех же счётчиков и открытость — коду отвечают, проверила); протухший абзац про `stop_requested` заменён на «ни одного члена впереди канона»; комментарий у теста строки библиотеки сужен до правды (сам тест не тронут) | + +⚠ **Два фикса из тринадцати пина НЕ имеют, и это подписано, а не скрыто.** **F4**: пин исполнением +НЕВОЗМОЖЕН — тестовые БД наливаются миграциями ДО данных, поэтому старая семантика задним числом +юнитом непроверяема; держится формулой и комментарием миграции. **F9**: вызов подметания ИЗ `main` +на буте не запинен — мутация «не звать на буте» ловится только чтением (сам подметатель запинен +`TestBootSweepsOrphanedCorrectionDocuments`). ### Два НЕСОГЛАСИЯ с диспозициями (норма «говори») @@ -1228,34 +1162,20 @@ FAIL/DATA RACE — 0; `golangci-lint` → **0 issues**; 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`) — файлы твоей зоны, чинить тебе. +Новые строки раунда — `PD-402`…`PD-413` (в том числе `PD-410`, архитектурная, отдельным паком) плюс +честная пере-редакция `PD-400.2` (мульти-репличный суб-кейс с ложным 503 назван прямо, оговорка +внесена и в комментарий ветки класса 12). Статусы строк, чьё лечение легло этим раундом, зона НЕ +переводила — закрытие есть акт лендинга; предмет и текущие статусы читаются в самом регистре, а +счёт — `counts.py --check`, поэтому числа того дня здесь не консервируются. -### Числа финальной батареи фикс-раунда (каждое — командой) - -`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 ??)**. +`go test ./... -race` с тремя гейтами → EXIT=0, 18 пакетов, 0 скипов, `FAIL|DATA RACE` — ноль; +линтер 0 issues. +⚠ **Промежуточная батарея №2 имела РОВНО ОДИН честный FAIL, и починен был КОД, а не тест:** старый +пин `TestResumeIsRefusedWhenTheBookHasAnotherLiveRun` поймал, что первый вариант гарда F12 +перекрывал канонное слово `run_in_flight` при живом ЧУЖОМ прогоне. Развёл слова (живой сосед → +`run_in_flight`, финишировавший → `ErrNotResumable`); тест не тронут. ## ВОРКФЛОУ-РЕВЬЮ ДЕРЕВА P9 ОТРАБОТАНО — 16 линз, оба отложенных MAJOR подтверждены замером, сводка находок для оркестратора (сессия платформы, 28.08) @@ -1311,17 +1231,17 @@ bankCountsTx; проекции без утечек словаря/путей; bu Дерево передаётся на лендинг. Опись: `git status --short -- platform/` → 31 изменённый + 9 новых файлов, все в зоне; вне `platform/` не тронуто ничего. -### Таблица комплектности против §3 (пункт → сделано → каким ИСПОЛНЕНИЕМ подтверждено) +### Что построено по §3 (чем доказано — пины в дереве и живой пробой `docs/p9/door-live-probe.md`) -| §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) | +| §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` | +| §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). Три ремеди («пере-реши»/«повтори то же»/«позови оператора») не сливаются | +| §3.2 синхронность | вызов синхронный, бюджет = `runBudget()` (60 с — класс вызовов движка); пер-книжный мьютекс `lockBook` в `runs.Service`, его берут corrections И `Start` И `Resume` (Start — та же гонка спавна, что resume); проверка живого прогона — ПОД мьютексом; гейт готовности книги (`readyToTranslate`) — как у Start (находка опровергателя) | +| §3.3 ключи | `TM_PLATFORM_ENGINE_KEYS_PATH` (абсолютный или отказ на буте; ⚠ суффикс `_PATH`, не `_FILE` — `*_FILE` в зоне значит «файл со значением секрета», гейт `TestEverySettingThisServiceReadsIsPrinted` это и поймал) → `runner.TranslateArgs` кладёт `--keys-file` ТОЛЬКО на `translate`; в окружение юнита ключи не кладутся; пусто = WARN на буте | +| §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-прогоны — обе базы пере-сняты верными предикатами, законченные — аппроксимация, названо в самой миграции) | +| §3.5 PD-399 | `pending_decisions`/`complete` сняты со всех трёх носителей (`BankCounts`+кадр `EventBank`, `wireBankPage`, подзапрос к мёртвой `bank_decisions` ушёл); пин ПЕРЕПИСАН на отсутствие | +| §3.6 конвенция пути | `runner.projectDB()` и парс `book.yaml` УДАЛЕНЫ; путь банк-экспорта берётся из конверта `artifacts.bank_export`, который движок публикует в `manifest --json` (движковая половина — d1eb8a9); `refreshBank` кормится манифестом той же refresh-пачки; движок без конверта = громкий отказ, долг ретраится | ### Числа сдачи (каждое — командой) @@ -1337,16 +1257,10 @@ bankCountsTx; проекции без утечек словаря/путей; bu - Миграции: `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` | + docs/architecture/14-api-contract`), вердикт по ДЕЛЬТЕ против чистой базы ТОЙ ЖЕ копии и по + ТОПИЧНОСТИ. Состав — комментарии `Mutation caught:` в самих пинах: слияние класса 15 в + `ErrBankUnavailable` · кортеж без `sense` · `KeysFile` не доезжает до argv · знаменатель полосы + снова `2×ceiling` · возвращённое пере-базирование на снятии стопа. ### Живой пробой (§4.2) — цепь срослась, и за $0 @@ -1529,10 +1443,8 @@ mutation-catch (спутать пары «числитель×база»); ко агентских посадок не сохранены, и **из 21 «пойман» конкретный тест называют 19, а 2 — нет**; их вердикты пере-ранить нельзя, только пере-посадить по описанию. Уникальных переживших инвариантов **15**, каждый несёт строку регистра. -⚠ **Метод-урок той же сдачи, и место ему не здесь:** обрезанный по ширине вывод грепа читается как -ОТСУТСТВИЕ совпадения — так зона объявила «грепа в промте ноль» на строке, которую греп нашёл. Дом -урока — `platform/docs/ENGINEERING_STANDARDS.md` (дисциплина доказательства грепом); перенос за тем, -кто правит тот файл. +⚠ **Метод-урок той же сдачи ПЕРЕНЕСЁН в свой дом 02.09** — `ENGINEERING_STANDARDS.md` §3 п.8, +дисциплина доказательства грепом (обрезанный по ширине вывод читается как отсутствие совпадения). **Об ошибках, которые вы назвали сами** (пять, от овер-атрибуции `PLATFORM_DIRECTION` до неполной описи): называть их — правильно, и опись, недобравшая три файла из пяти, действительно стоила бы @@ -1548,116 +1460,71 @@ mutation-catch (спутать пары «числитель×база»); ко намеренная это семантика пере-разреза или дефект, и что делать со счётчиками, которые после этого показывают ноль сделанного на книге, где работа была. -- **ПАК P8-REVIEW ОТРАБОТАН (24.08) — четыре оси прочитаны, кода не тронуто.** +- **ПАК P8-REVIEW ОТРАБОТАН (24.08) — четыре оси прочитаны, кода не тронуто.** Ратификация — + **D39.159**; двадцать одна находка живёт строками `PD-375`…`PD-398`, воспроизводящие скрипты и + снимки — `docs/p8-review/` (его `README.md` — карта каталога). Здесь остаётся ровно то, чего нет + ни в ноте, ни в строках: - **Ось → чем закрыта → находки.** Предмет каждой оси взят из промта целиком; ниже только отклонения. - - 1. **Деньги и леджер.** Обязательная норма исполнена дважды и на НЕтривиальном состоянии — + 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`.** + замера, оба пути сошлись). Вторая половина нормы (леджер = НИЖНЯЯ граница) проверена отдельно: + расход СВЕРХ холда каппится и живёт только текстом в `note`, который не читает ни один путь + кода — `internal/pgstore/credits.go:225` плюс ноль `SELECT` по колонке. **Это проектное решение, + строкой не заведено.** + 2. **Метрики — ответ на вопрос оси «что оператор понял бы по этой дельте»: почти ничего.** Переход + в `stalled` по одной `/metrics` виден; отказ свипа — нет (меняется только гистограмма + длительности); гейджи при отказе чтения замирают без признака устаревания; инстанс, у которого + чтение не удалось ни разу, отдаёт нули как здоровье. Дифф трёх снимков — `axis4-metrics/`. + ⚠ Единственная находка пака, **опровергнутая** рефутером по всем четырём состояниям: случаи, + где `run abandon` ОТКАЖЕТ, оператор различает — колонка SPENT печатает «?» ровно при пустой + базовой линии, биекция. - Сверх осей — из сверки регистра с деревом: `PD-396` (мёртвые поля шва) и `PD-398` (гейт формы - регистра ловит недостачу ячеек, но не избыток). + **Посадки координатора — 16 (M1–M16), вторым рубежом поверх агентских. Поимённо они живут не + здесь, а в харнесе: `docs/p8-review/plant.py` знает каждую, вердикты — `mutations.log` · + `mutations-full.log` · `mutations-round2.log`.** Убито семь, ВЫЖИЛО ДЕВЯТЬ, и каждый выживший + получил строку: `PD-394` · `PD-389` · `PD-376` · `PD-380` · `PD-382` · `PD-383`. Все девять + пере-проверены на ПОЛНОЙ батарее против чистой базовой линии ТОЙ ЖЕ копии, не по exit-коду — + правило вердикта записано нормой в `ENGINEERING_STANDARDS` §3 п.3, а цена его забывания — в + `PD-382` и `PD-395`. - **Таблица посадок — мои, вторым рубежом поверх агентских** (`docs/p8-review/plant.py`, логи - `mutations.log` · `mutations-full.log` · `mutations-round2.log`): + **Сверка реестра с деревом дала 17 ⚠-дописок**, и каждая живёт в СВОЕЙ строке регистра (греп + `ОСПОРЕНО(` — конвенция токена в шапке регистра): статусы пак не менял, потому что смена статуса + есть акт лендинга. - | # | ось | что посажено | какой пин обязан был упасть | упал | - |---|---|---|---|---| - | 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` | + **Аддендумы владельца по ходу (§5 промта), дословно:** «Архитектурно чистое решение по `PD-395`?» + → диспозиция сменилась с «править гейт» на «править рецепт», а лечение переехало из эфемерного + носителя (промт архивируется) в `ENGINEERING_STANDARDS` §3 · «Посоветуйся со старшим; веди через + существующего агента» → два захода старшей модели, оба абсорбированы · «Доводи до конца» → + рефутер по восьми утверждениям сверки реестра и редакторский аудит сдачи; оба нашли дефекты. - Пол по каждой оси выполнен — колонка «ось». Все девять выживших пере-проверены на ПОЛНОЙ батарее - против чистой базовой линии ТОЙ ЖЕ копии, не по exit-коду. - - ⚠ **Правило вердикта, которое пере-ранящему знать обязательно:** судить по ДЕЛЬТЕ против чистой - копии И по ТОПИЧНОСТИ упавшего теста, а не по цвету батареи. Цена забывания оплачена здесь дважды: - известный флейк `PD-369` дал ложное «пойман», а первая редакция `mutations-full.log` печатала `RED` - там, где мутация выжила. Правило записано в `ENGINEERING_STANDARDS` §3 п.3, `PD-382` и `PD-395`. - - **Реестр против дерева — 17 ⚠-дописок.** Строка `open`, чьё лекарство уже в дереве, отправляет - следующий пак чинить построенное. Предлагаю ЗАКРЫТЬ: `PD-23`, `PD-203`, `PD-166`. Сузить, но НЕ - закрывать: `PD-162`, `PD-368`, `PD-374`. Пере-формулировать: `PD-6`, `PD-157`, `PD-168`, `PD-90`, - `PD-101`. Свести с условием, названным в дописке: `PD-251`↔`PD-42`, `PD-250`↔`PD-179`. Помечены - токеном `ОСПОРЕНО(PD-N)` — статусы паком НЕ менялись: `PD-159`, `PD-293`, `PD-361`. Конвенция - токена записана в шапку регистра. - - **Аддендумы владельца по ходу (§5 промта) — три, результат:** - 1. «Архитектурно чистое решение по `PD-395`?» → строка ПЕРЕПИСАНА, диспозиция сменилась с «править - гейт» на «править рецепт», рецепт исправлен и проверен исполнением, а лечение переехало из - эфемерного носителя (промт архивируется при лендинге) в `ENGINEERING_STANDARDS` §3. - 2. «Посоветуйся со старшим; веди через существующего агента» → два захода старшей модели, оба - абсорбированы; дальше вёл сообщением, а не новым агентом. - 3. «Доводи до конца» → рефутер по восьми утверждениям сверки реестра (последний рубеж без - адверсариальной проверки) и редакторский аудит сдачи; оба нашли дефекты, оба закрыты. - - ⚠ **Мои ошибки, которые нашли не я, и они того же класса, который пак ловил.** (а) Сослался на + ⚠ **Мои ошибки, которые нашёл не я, и они того же класса, который пак ловил.** (а) Сослался на `PLATFORM_DIRECTION` §3 как на ратифицированное направление по `oapi-codegen`, не сверив, что оно - ПЕРЕ-ПОДПИСАНО `D39.132` в «кандидат» и P7 решил не брать. (б) Дописка к `PD-166` описывала - механизм, недостижимый в сегодняшнем коде. (в) Сужения четырёх строк оси 3–4 оказались КРУГОВЫМИ — - каждое опиралось на поверхность, несостоятельность которой доказывает соседняя строка того же пака; - из-за этого `PD-385` стояла minor. (г) `PD-379` несла вес major, а лежала в секции minor. (д) Опись - изменённого называла два файла из пяти — лендер недобрал бы правку НОРМЫ. Всё исправлено; называю, - потому что необъявленная ошибка автора — это находка, которой нет. + ПЕРЕ-ПОДПИСАНО `D39.132` в «кандидат». (б) Дописка к `PD-166` описывала механизм, недостижимый в + сегодняшнем коде. (в) Сужения четырёх строк оказались КРУГОВЫМИ — каждое опиралось на поверхность, + несостоятельность которой доказывает соседняя строка того же пака. (г) `PD-379` несла вес major, а + лежала в секции minor. (д) Опись изменённого называла два файла из пяти — лендер недобрал бы правку + НОРМЫ. Всё исправлено; называю, потому что необъявленная ошибка автора — это находка, которой нет. - **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» на месте — правку комментария бенчмарка приёмка приняла. + **D39.154**, здесь не пересказывается. Зоне важно то, чего в ноте нет: - **Правку зоной ЧУЖОГО документа** (пере-нацеливание двух моих якорей в промте читающего пака - после переезда констант) **утверждаю и не откатываю**: якоря умерли по вине пака, пак их починил - и раскрыл это сам, вместо того чтобы обойти красный гейт. Это ровно то поведение, которого норма - и требует, — так и делайте дальше. - - **«Скипов 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). + после переезда констант) оркестратор **утвердил и не откатил**: якоря умерли по вине пака, пак их + починил и раскрыл это сам, вместо того чтобы обойти красный гейт. Это ровно то поведение, + которого норма и требует, — так и делайте дальше. + - **«Скипов 0» на хосте приёмки НЕ воспроизвелось — три скипа**, потому что `systemdOrSkip` гейтит + три теста `internal/runner` достижимостью пользовательского менеджера systemd. Не регрессия и не + ошибка отчёта; незакрытым осталось то, что рецепт объявлял у батареи ДВА гейта, а их три — + `PD-374`. + - **Одиннадцать посадок приёмки вне списка зоны:** убито восемь, выжили три — `StalledAfter` + законно (носитель один), `maxAttempts` и `truncateReason` дали `PD-373`/`PD-372`; плюс `PD-371` + вне карты пака. ⚠ Две первых редакции посадок приёмки дали ложное «выжила» — гонялся не тот + пакет; пере-прогнано. Названо, потому что необъявленная ошибка харнесса приёмки — находка, + которой нет. - **Открытым уезжает `PD-370`** (контрактная половина) — не работа зоны, закрывать её здесь было бы - подгонкой под критерий приёмки. Согласен с вашей диспозицией. + подгонкой под критерий приёмки. ⚠ **Порядок паков вышел ОБРАТНЫМ очереди:** промт P8-FIX требовал запуска ПОСЛЕ читающего пака, - запущен был раньше. В плюс — названная цена перестановки («блокер живёт всё время читающего пака») - НЕ заплачена. В минус — §4.7 релея пуст, и читающий пак пойдёт по только что переписанному коду; - его промт про это предупреждён баннером. + запущен был раньше. Названная цена перестановки («блокер живёт всё время читающего пака») НЕ + заплачена, но §4.7 релея остался пуст. - **Эра пака P8-FIX (21–22.08) — В АРХИВЕ.** Отчёт пака, обе волны ревью, спил пер-термной подписи, инвентарь каналов и obstacle — [archive/platform-PROGRESS-P8.md](archive/platform-PROGRESS-P8.md). Ратификация — D39.154, лендинг `31f1f82`. Живое из этой эры: четыре открытые строки регистра (`PD-370` мажор — контрактная половина · `PD-371` · `PD-372` · `PD-373`/`PD-374`), инвентарь каналов шва в `STACK_DECISIONS.md`, граница sqlc в `BACKLOG.md` П-19. @@ -1694,22 +1561,14 @@ mutation-catch (спутать пары «числитель×база»); ко **Комментарий в коде — ваш**, я его не трогаю. Правка за зоной. -### Пинг оркестратора №22 — 02.09.2026 (ревизия доков, находка N018) +### Пинг оркестратора №22 — 02.09.2026 (находка N018) — ИСПОЛНЕН зоной 02.09 -**`.bank-stop.json` БОЛЬШЕ НЕ СУЩЕСТВУЕТ**, а ваш живой док его описывает как существующий. - -Файл снесён вместе с писателем и четырьмя тестами при постройке входной двери шва (**D39.158**). -Проверено моей командой: `grep -rc 'bank-stop.json' backend/ --include='*.go'` → **ни одного вхождения**; -холодный прогон 31.08 подтвердил это на диске. Свой носитель (`docs/PROGRESS.md`, строка 157) я поправил. - -**Ваш носитель:** `platform/docs/STACK_DECISIONS.md`, строка таблицы сайдкаров с -`.bank-stop.json` и писателем `pipeline/mining.go` — сайдкара нет, писателя нет. - -⚠ **Что править НЕ надо** (проверено, это законная история в прошедшем времени, а не дрейф): -`docs/architecture/18-bank-ontology.md`, `backend/docs/SEAM_FIX3_PLAN.md`, -`backend/docs/SEAM_PACK_FINDINGS.md` — там файл назван как УРОК о снесённом механизме. - -Кода не трогаю; правка за зоной. +`.bank-stop.json` снесён вместе с писателем при постройке входной двери шва (**D39.158**); +`grep -rc 'bank-stop.json' backend/ --include='*.go'` → ноль вхождений. Строка канала убрана из +`STACK_DECISIONS.md` «Инвентарь каналов движка». ⚠ Тем же заходом починены две СОСЕДНИЕ протухшие +строки той же таблицы, которых пинг не называл: `.mined-signature.yaml` и `.auto-bank.yaml` пишутся +теперь `writeFileAtomic` (греп `signatureMapPath` / `autoBankPath` в `backend/internal/pipeline/mining.go`), +то есть АТОМАРНО — правило «на живом прогоне не брать» осталось только у `.bank-stop.txt`. ### Пинг оркестратора №22 — 02.09.2026 (ревизия доков, батч Б3 «денежный путь») @@ -1733,42 +1592,38 @@ D39.179 п.1 ратифицировал: DeepSeek пере-пинен под ц **Пинг оркестратора №15 (09.08, D39.122): движковый пак «блокеры контракта» ПРИНЯТ и заленден `0e69bc1` — пять поверхностей для платформы существуют. ФИНАЛЬНЫЕ формы (менялись трижды за приёмку — старые в переписке игнорировать):** - **Манифест глав:** `.manifest.json`, `manifest_version: "tm-manifest-v2"` (v1 движок сам отклоняет); id главы = 16 hex (стабилен через пере-нарезку); **`unit.id = ::`** — тег разреза 8 hex, при любой смене нарезки/данных пары unit.id УМИРАЮТ намеренно (id жив ⇒ якорь цел); поле `heading` — ВРЕМЕННЫЙ рендер движка «Глава N», НЕ метка книги (решение владельца 09.08; настоящие заголовки — строка 160 бэклога движка). $0-команда `tmctl manifest` строит дерево до первого прогона (первое касание создаёт БД проекта — то же поведение, что у status). - **Прогресс:** `status --json` → `progress: {draft:{done,total}, edit:{done,total}}` на книге и в каждом элементе `chapters`; `done` = «разрешено волной» (ok/flagged/skipped); волна, которой нет, — `total: 0`; процент не отгружается — собирает клиент. -- **Банк:** `.bank.json` (весь банк тремя статусами; id термов — длино-префиксированный хеш ключа уникальности, стабилен через пересборку) и `.bank-stop.json` (полная таблица подписи; `conf: null` ≠ 0). ⚠ **ИСПРАВЛЕНО оркестратором №18 (20.08, линза шва P7): «ВСЕ сайдкары атомарно» — НЕВЕРНО.** Атомарны (temp+`Sync`+rename, `backend/internal/pipeline/artifact.go:24-59`) ровно три — `manifest.json`, `bank.json`, `bank-stop.json`, — и читать во время прогона можно ИХ. `bank-stop.txt`, `mined-signature.yaml`, `auto-bank.yaml` пишутся `os.WriteFile` с усечением первым делом: читателей у них сегодня нет, и сессия, взявшая любой по прежней формулировке, получила бы усечённый документ на живом прогоне. -- **Потолок (строка 145): `tmctl translate|redrive --ceiling-usd ` — это КНИЖНЫЙ ПОТОЛОК В СИЛЕ, не бюджет прогона** (ратифицировано D39.122): леджер сравнивает значение с накопленным committed+reserved книги. Пересчёт пользовательского «прирост в главах» (D39.110) в абсолют — ОБЯЗАННОСТЬ платформы. **⚠ АМЕНДИРОВАНО D39.123 (PD-158): формула = `committed_usd + прирост×оценка`, БЕЗ reserved** — read-only `status` показывает leftover-reserved, который `store.Open` зануляет до первой судимой резервации; включение переплачивало бы запасом сверх холда (исполнено обеими формулами против гейта). `reserved_usd` читается обязательным полем как гард присутствия и улика leftover. Значение ниже уже потраченного откажет первой же резервации. День-потолок не перекрывается. Опция по желанию зоны: движок готов провести `--ceiling-usd` и в `status`, чтобы мониторинг видел действующий потолок capped-прогона (сегодня status показывает книжный) — скажите, заведём строку. +- **Банк:** `.bank.json` — весь банк тремя статусами; id термов — длино-префиксированный хеш ключа уникальности, стабилен через пересборку. ⚠ **Второй сайдкар этого пункта, `.bank-stop.json`, БОЛЬШЕ НЕ СУЩЕСТВУЕТ** (снесён D39.158). ⚠ И «ВСЕ сайдкары атомарно» было НЕВЕРНО (испр. оркестратором №18, 20.08, линза шва P7): атомарность каждого канала по отдельности — единственным носителем в `STACK_DECISIONS.md`, «Инвентарь каналов движка», и брать неатомарный на ЖИВОМ прогоне нельзя. +- **Потолок (строка 145): `--ceiling-usd` — КНИЖНЫЙ потолок в силе, не бюджет прогона** (D39.122), формула пересчёта амендирована D39.123 по `PD-158`. Разбор и три следствия — `STACK_DECISIONS.md` §21, здесь не дублируются. ⚠ **Живое из пинга — открытое предложение движка:** он готов провести `--ceiling-usd` и в `status`, чтобы мониторинг видел действующий потолок capped-прогона (сегодня `status` показывает книжный) — скажите, заведём строку. -## Пинг оркестратора №16 — 09.08.2026 (форма DEFECT_REGISTER, строка 167) +## Пинги оркестратора №16 и №17 — 09–15.08.2026, ИСПОЛНЕНЫ; живым остаётся одно -ИСПОЛНЕН: реестр разложен на секции (открытые по весу · принятый риск · закрытые по эрам паков) при -сохранённой построчной форме `| PD-N | … |`, на которой ключуется `docs/scripts/counts.py`. Заказ и -его причина — **D39.126 §3** («строка 167 пере-скоуплена в остаток: … DEFECT_REGISTER секциями»), не D39.125. +**№16 (форма регистра, строка 167 единого, заказ — D39.126 §3):** реестр разложен на секции +(открытые по весу · принятый риск · закрытые по эрам паков) при сохранённой построчной форме +`| PD-N | … |`, на которой ключуется `docs/scripts/counts.py`. -## Пинг оркестратора №17 — 15.08.2026 (migrate принят, D39.134 — движковая половина самолечения ФИНАЛЬНА) +**№17 (`tmctl migrate` принят и заленден `d55edd4`, D39.134):** exit 13 = `schema_mismatch` +финализирован, токен `schema_mismatch found=N expected=M` на stderr стабилен, write-путь тоже +отказывает БД новее бинаря — движок под ногами больше не сдвинется, и `PD-201` («поймал 13 → +migrate → повтор») можно строить; строка открыта и несёт предмет. Три хвоста зоны, найденные той же +приёмкой (мёртвая цитата текста ошибки схемы в рантбуке и в строке П-1; образец в `tmplatformctl`, +зовущий голый `tmctl` из PATH), закрыты правками и проверяются грепом: `grep -rn 'expects vM' +platform/deploy platform/BACKLOG.md` — ноль строк; рантбук несёт живой токен +(`platform/deploy/README.md:187`=`schema_mismatch found=N expected=M`); образец несёт +версионированный путь (`platform/cmd/tmplatformctl/runs.go:52`=`The VERSIONED path and never a bare`). -Бэкенд-пак `tmctl migrate` принят и заленден (`d55edd4`): exit 13 = `schema_mismatch` финализирован, -токен `schema_mismatch found=N expected=M` на stderr стабилен, write-путь тоже отказывает БД новее -бинаря — то есть движок под ногами больше не сдвинется, и `PD-201` («поймал 13 → migrate → повтор») -можно строить; строка регистра открыта и несёт предмет. Три хвоста зоны, найденные той же приёмкой -(мёртвая цитата текста ошибки схемы в деплой-рантбуке и в строке П-1 бэклога; образец в -`tmplatformctl`, зовущий голый `tmctl` из PATH), с тех пор закрыты правками, и это проверяется грепом: -мёртвой цитаты текста ошибки схемы («schema vN … expects vM») нет ни в рантбуке, ни в бэклоге зоны -(`grep -rn 'expects vM' platform/deploy platform/BACKLOG.md` — ноль строк), рантбук несёт -живой токен (`platform/deploy/README.md:187`=`schema_mismatch found=N expected=M`), строка П-1 бэклога -закрыта и адресует рантбук, а образец несёт версионированный путь -(`platform/cmd/tmplatformctl/runs.go:52`=`The VERSIONED path and never a bare`). +⚠ **Живым остаётся мнение оркестратора:** прогнать деплой-рантбук end-to-end на дев-стенде +НАСТОЯЩИМ `migrate` — обязательное предусловие первого выката. Журнал зоны фиксировал, что живьём +команда не гонялась. -## Пинг оркестратора №17 (второй) — 15.08.2026 (S4 принят, контракт 0.2.3 в каноне — пять строк вашего регистра протухли; аудит корпуса) +## Пинг оркестратора №17 (второй) — 15.08.2026 (S4 принят, контракт 0.2.3 в каноне) — ИСПОЛНЕН -Фронт S4 принят и заленден (**D39.135**), контракт 0.2.3 в каноне. Пять статусов регистра (`PD-172`, -`PD-173`, `PD-174`, `PD-180` по диспозиции D39.130 п.2в; `PD-199` по D39.132 п.2а) с тех пор приведены — -все пять стоят закрытыми эрой P7. Обязательство 0.2.3 (`BookIntake.title` и проекция -`books.reject_reason` на провод, D39.135 п.2в) исполнено — `reject_reason` живёт членом ответа книги. -Три кандидата аудита 15.08 (`PD-219` упавший дрейн теряет хвост `unit_resolutions` · `PD-217` книга на -вечном холде блокирует апгрейд движка · `PD-162` удалённый каталог книги = вечный прогон с открытым -холдом) ОСТАЮТСЯ ОТКРЫТЫМИ строками регистра — предмет там, не здесь. -⚠ **Живым остаётся одно:** ваш журнал фиксировал «`tmctl migrate` живьём не гонялся», команда с D39.134 -существует, и мнение оркестратора — прогнать деплой-рантбук end-to-end на дев-стенде настоящим migrate -как обязательное предусловие первого выката. +Пять статусов регистра (`PD-172`, `PD-173`, `PD-174`, `PD-180` по D39.130 п.2в; `PD-199` по D39.132 +п.2а) приведены — все пять стоят закрытыми эрой P7; обязательство 0.2.3 (`BookIntake.title` и +проекция `books.reject_reason` на провод, D39.135 п.2в) исполнено. Три кандидата аудита 15.08 +ОСТАЮТСЯ открытыми строками, и предмет там, не здесь: `PD-219` (упавший дрейн теряет хвост +`unit_resolutions`) · `PD-217` (книга на вечном холде блокирует апгрейд движка) · `PD-162` +(удалённый каталог книги = вечный прогон с открытым холдом). ## Пинг оркестратора №17 (третий) — 16.08.2026 (контракт-ревью принято D39.138: ваша половина батча 0.3.0, PD-104 закрыт, попутные находки) @@ -1786,13 +1641,13 @@ D39.179 п.1 ратифицировал: DeepSeek пере-пинен под ц **Вердикт: ПРИНЯТЬ С ФИКС-ЛИСТОМ.** Панель шести линз в изолированных копиях (слепая · контракт-конформность · деньги · шов · вне карты · ревью канона 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 честный. +**Пере-проверено МОЕЙ рукой (заявление = команда):** батарея с обоими гейтами — 18 пакетов, EXIT=0, скипов 0, линтер 0 issues · `tmplatformctl books --migratable` живой (exit 0, «resumable run» на стендовой книге) · миграционный манифест сходится, released-миграции не тронуты. -**Замер, обосновавший разворот 00016→00022, пере-выведен независимо** (свой бенчмарк на трёх вариантах в копии дерева): джойн **14.6 мс** · счётчик **5.3 мс** · без счётчика **2.9 мс**. Порядок и вывод подтверждаются, разворот законен. ⚠ Процент назван в ТРЁХ носителях тремя разными числами (журнал «83%», `perf_test.go:13` «96%», миграция 00022 «16.8 против 3.4, джойн сам по себе 9 мс»); мой замер даёт ≈80%, а «96%» ни из чего не выводится — один носитель на факт, остальные указывают. +**Замер, обосновавший разворот 00016→00022, пере-выведен независимо** (свой бенчмарк на трёх вариантах в копии дерева): джойн **14.6 мс** · счётчик **5.3 мс** · без счётчика **2.9 мс**. Порядок и вывод подтверждаются, разворот законен. ⚠ Стоявшее здесь заключение «96% ни из чего не выводится» я СНЯЛ САМ — оно неверно, разбор в п.9 фикс-листа ниже: носители мерили РАЗНОЕ (холодный корпус против вакуумированного) и все честны. -**Собственные посадки мутаций ВНЕ вашего списка — 8, поймано 7:** долг не поставлен третьей концовкой ✅ · `edit_wave` не монотонен ✅ (⚠ сообщение пина печатает указатель вместо значения — `readmodel_test.go:918`) · снят `Vary: Accept-Encoding` ✅ · HEAD лишён валидатора ✅ · `reopen` снимает стоп банка кому угодно ✅ · погашение долга без сверки метки ✅ · вынос метки долга из закрывающей транзакции во второй оператор ✅ (техника `xmin` §36 работает, проверено исполнением). +**Собственные посадки мутаций ВНЕ вашего списка — 8, поймано 7** (состав — комментарии `Mutation caught:` в самих пинах; техника `xmin` §36 работает, проверено исполнением). -⚠ **НЕ поймано — дыра:** снятие `structure_version` И `revision` из `emitFrame` (`pgstore/events.go:72-73`) проходит ВСЮ батарею. Это два поля, которые канон требует на КАЖДОМ кадре (`EventBase`), и ваш же комментарий это утверждает. По вашей норме PD-1 свойство без пинящего теста считается НЕ закрытым. +⚠ **НЕ поймано — дыра, с тех пор ЗАКРЫТАЯ (п.2 фикс-листа):** снятие `structure_version` И `revision` из `emitFrame` проходило ВСЮ батарею — два поля, которые канон требует на КАЖДОМ кадре (`EventBase`). Пины стоят в `pgstore/events_test.go` с названной пойманной мутацией. ### Фикс-лист приёмки — завести строками СВОЕГО бэклога и регистра (зона моя не пишет)