Slice the P8 era and the P7 leftovers out of the zone journal after moving what only it carried into durable homes
This commit is contained in:
parent
ca886c0f61
commit
5a700e03b4
4 changed files with 696 additions and 689 deletions
|
|
@ -173,7 +173,7 @@
|
|||
| PD-51 | bug | minor | `internal/httpapi/serve.go:118-127`, `STACK_DECISIONS §12` | **Механизм заявлен неверно.** Утверждение «`ReadTimeout` убил бы и поток, поэтому стриминговый хендлер ОБЯЗАН снять read-дедлайн» на Go 1.26.5 не подтверждается: `connReader.startBackgroundRead` сам делает `SetReadDeadline(time.Time{})` (`net/http/server.go:687-698`) и для запроса без тела вызывается ДО хендлера (`:2062`). Проверено исполнением на трёх формах запроса (GET без тела · POST с непрочитанным телом · POST с вычитанным телом) — поздний кадр доезжает во всех шести комбинациях, звали `ClearReadDeadline` или нет. Следствие: `TestStreamOutlivesReadTimeout` НЕ МОЖЕТ упасть от выхолащивания `ClearReadDeadline` (проверено); он пинит только `Unwrap` (эта посадка ловится). Код безвреден, ложны обоснование и строка в таблице пинов — **закрыто, и вывод приёмки уточнён исполнением:** механизм подтверждён (`startBackgroundRead` снимает дедлайн сам, `server.go:687-698`, для запроса без остатка тела — до хендлера, `:2059-2062`; по ходу хендлера не перевзводится — проверено по всем call sites). Но «код безвреден» неверно: см. PD-63. `ClearReadDeadline` УДАЛЁН, `STACK_DECISIONS §12` переписан, `TestStreamOutlivesReadTimeout` переписан на настоящее свойство (поток переживает `Read` БЕЗ действий хендлера) и пинит `Unwrap` через ошибку `Flush` | fixed(P2, дерево сессии) | приёмка P1 (посадка M24/M42 + отдельная проба) |
|
||||
| PD-52 | hardening | minor | `internal/pgstore/credits.go:233-243` | Порядок блокировок (PD-26) не запинен ни одним тестом — снятие `lockBalance` из `closeReservation` батарею переживает. Дефект воспроизведён приёмкой НЕЗАВИСИМО, в форме, которая действительно даёт цикл: конкурентные `Settle(run-1)` и повторный `Hold(run-1)` — **2 взаимоблокировки на 150 раундов с инверсией, 0 с фиксом**. Регрессионный тест написан приёмкой и лежит готовым к вставке в `docs/platform-PROGRESS.md`, раздел «Ратификация приёмкой P1». ⚠ Замер сессии «41 на 300» воспроизвести не удалось — их нагрузка не описана; принимается СО СЛОВ — **закрыто:** тест приёмки вставлен как `pgstore.TestHoldAndSettleOnTheSameAttemptDoNotDeadlock`. ⚠ Замер приёмки не копировался, а ПЕРЕПРОВЕРЕН на своём стенде (PostgreSQL 18.4): с инверсией падает 5 прогонов из 5, 5–10 взаимоблокировок на 150 раундов; с фиксом 5 прогонов из 5 зелёные. Замер сессии P1 «41 на 300» так и не воспроизведён и остаётся СО СЛОВ | fixed(P2, дерево сессии) | приёмка P1 (посадка M07 + собственная репродукция) |
|
||||
| PD-53 | hardening | info | `internal/httpapi/server.go:73-75` | `DefaultMaxBody` не запинен: поднятие лимита поддерева до 1 ГиБ батарею переживает. Пер-маршрутность лимита (PD-35) — тоже только на ревью — **закрыто:** `httpapi.TestBodyCapIsPerRouteBecauseNestingOnlyTightens` фиксирует исполнением ПРИЧИНУ пер-маршрутности — вложенный БОЛЬШИЙ лимит не поднимает внешний, — поэтому возврат общего слоя молча урезал бы аплоуд-маршрут; `TestDefaultBodyCapStaysAContractSizedNumber` держит дефолт в полосе контрактного размера (посадка «1 ГиБ» падает), не превращаясь в change-detector на точное число | fixed(P2, дерево сессии) | приёмка P1 (посадка M26) |
|
||||
| PD-54 | bug | minor | `docs/platform-PROGRESS.md:329-353` | В журнале ДВЕ несовместимые формы `GET /v0/usage`: новая кредитная (строка 172) и старая подписочная (строка 329) с `resets_at`, `windows[{period}]` и хранением в `usage_windows` — таблице, которую снесла миграция `00006`. Секция P0-эры не помечена superseded, а S3 идёт читать журнал именно за формой ручки — **закрыто:** подписочное тело ответа УДАЛЕНО из журнала, а не помечено баннером: S3 идёт туда за формой ручки и скопировал бы тело. Осталась одна форма — кредитная, в разделе «Что предлагаем в спеку (S3)»; из П-5 сохранены абзацы, не зависящие от модели денег, ссылка на хранение переведена на `credit_ledger` | fixed(P2, дерево сессии) | приёмка P1 (свип доков) |
|
||||
| PD-54 | bug | minor | зонный журнал, секция эры P0 (⚠ якорь на строку снят 22.08: описанное тело было УДАЛЕНО при закрытии строки, а сам журнал с тех пор дважды срезан в слайсы — адресовать было нечего) | В журнале ДВЕ несовместимые формы `GET /v0/usage`: новая кредитная (строка 172) и старая подписочная (строка 329) с `resets_at`, `windows[{period}]` и хранением в `usage_windows` — таблице, которую снесла миграция `00006`. Секция P0-эры не помечена superseded, а S3 идёт читать журнал именно за формой ручки — **закрыто:** подписочное тело ответа УДАЛЕНО из журнала, а не помечено баннером: S3 идёт туда за формой ручки и скопировал бы тело. Осталась одна форма — кредитная, в разделе «Что предлагаем в спеку (S3)»; из П-5 сохранены абзацы, не зависящие от модели денег, ссылка на хранение переведена на `credit_ledger` | fixed(P2, дерево сессии) | приёмка P1 (свип доков) |
|
||||
| PD-55 | bug | info | `deploy/tmplatformd.service` | `MemoryMax=2G` объявлен как «bounds the control plane», но ограничивает cgroup ЮНИТА — а по собственному аргументу этого же файла (закрытие PD-13) в этом cgroup живёт каждый ребёнок-`tmctl`. Значит потолок общий на платформу и все идущие прогоны, и OOM-killer выберет самый жирный процесс — движок, который держит ЭКСКЛЮЗИВНЫЙ лок на файле проекта: ровно тот исход, ради которого запрещён SIGKILL. То же про `TasksMax=512`. Латентно до появления воркера. ⚠ Под systemd не проверялось (нет sudo) — вывод из семантики `MemoryMax=`, не из замера — **закрыто:** семантика сверена по man 5 systemd.resource-control («absolute limit on memory usage of the executed processes in this unit… out-of-memory killer is invoked inside the unit»). `MemoryMax=2G` заменён на `MemoryMax=80%` — потолок машины, а не сервиса, как «last line of defense» и без знания о железе; `TasksMax=512` оставлен с честным комментарием, что покрывает платформу и прогоны вместе; ограничение ОДНОГО прогона названо работой воркера (transient scope). Побочно найдено и закрыто следствие, которого в этой строке не было, — PD-64. ⚠ Под systemd не запускалось (нет sudo); `systemd-analyze verify` (systemd 259) — exit 0 | fixed(P2, дерево сессии) | приёмка P1 (ревью деплой-юнита) |
|
||||
| PD-56 | bug | info | `internal/pgstore/credits.go:35-63` | `Grant`/`Adjust` на несуществующий аккаунт отдают оператору сырую ошибку Postgres с именем констрейнта (`credit_ledger_user_id_fkey`), тогда как `Balance` на том же входе отдаёт `ErrNoAccount`. Живая проба CLI. Косметика админ-поверхности, но опечатка в id читается как поломка БД — **закрыто:** `appendLedger` мапит нарушение `credit_ledger_user_id_fkey` в `ErrNoAccount`; `pgstore.TestMoneyOperationsAgreeOnAMissingAccount` требует одного ответа от `Grant`/`Adjust`/`Balance`/`ReadAccount`. Посадка «убрать сверку констрейнта» падает | fixed(P2, дерево сессии) | приёмка P1 (живая проба CLI) |
|
||||
| PD-57 | hardening | minor | `internal/login/login.go:239-242` | **Защита от IdP mix-up не та, что требует действующая норма.** RFC 9700 §2.1 (OAuth Security BCP, янв. 2025) — клиент SHOULD применять параметр `iss` из авторизационного ответа (RFC 9207) либо иной контрмер НА ОСНОВЕ `iss`; MAY — различные redirect URI на провайдера. Реализована собственная сверка `st.Provider` с `h.cfg.Provider`, а внутри одного хендлера это сравнение конфигурации с самой собой: `start` пишет туда то же значение. `iss` авторизационного ответа не читается вообще (`iss` ID-токена библиотека проверяет — это другой шаг и другой момент). Сегодня не эксплуатируемо: провайдер один, код всегда редимится у него же. Заведено потому, что регистр объявляет PD-32 закрытием «класса IdP mix-up», а против нормы это неверно, и при втором провайдере выбор (`iss` или раздельные redirect URI) должен быть ОСОЗНАННЫМ, а не побочным эффектом конфигурации — **закрыто реализацией нормы, а не обещанием.** Первоисточники сверены: RFC 9700 §4.4.2 («When an OAuth client can only interact with one authorization server, a mix-up defense is not required» — то есть СЕГОДНЯ несоответствия нет, требование включается со вторым сервером), §4.4.2.2 объявляет раздельные redirect URI фолбэком («SHOULD therefore only be used if other options are not available»); альтернатива «`iss` из ID-токена» нам не подходит — при чистом code flow токен приходит уже ПОСЛЕ отдачи кода. Выбран `iss` авторизационного ответа: **Google его шлёт** (`authorization_response_iss_parameter_supported: true`, сверено живьём). Сделано: `auth_states.issuer` (миграция 00008), сверка до обмена кода, отказ на СОРВАННОМ параметре у поддерживающего провайдера (RFC 9207 §2.4). Пин — `login.TestAuthorizationResponseIssuerIsChecked` (4 случая); посадки «убрать вызов», «убрать ветку несовпадения», «убрать ветку сорванного параметра», «потерять issuer в сторе» падают | fixed(P2, дерево сессии) | приёмка P1 (сверка с RFC 9700 §2.1 / RFC 9207) |
|
||||
|
|
|
|||
|
|
@ -248,3 +248,196 @@ python3 docs/scripts/counts.py # регистр: 256
|
|||
проверки резки прозы.
|
||||
5. **Плотность комментариев** (PD-255): пак вышел за норму «одна-две строки почему». Часть прозы
|
||||
несущая, часть — эссе; прошу решение владельца о норме, а не тихую подрезку в следующем паке.
|
||||
|
||||
## ⟶ Дослано 22.08 оркестратором №18: записи акта 5, оставшиеся в живом журнале
|
||||
|
||||
> Заголовок живого журнала «Эра пака P7 — в архиве» утверждал, что выселено всё, а ~185 строк
|
||||
> остались на месте. Досылаю их сюда без правки тел.
|
||||
|
||||
- **P7, акт 5 — ЗАКРЫТ. Дерево готово к приёмке и лендингу.**
|
||||
Батарея: 18 пакетов, exit 0, скипов 0, линтер 0 issues, тестовых функций **532** (⚠ было «533» — испр. оркестратором №18 20.08: канонической командой D39.121 `grep -rh '^func Test' platform --include='*_test.go' | wc -l` выходит 532; расхождение на единицу, ни на что не влияет, но число было заявлено без команды). Регистр — 327
|
||||
строк, открытых 65 (13 minor, 52 info), **major и BLOCKER нет**. Дерево не закоммичено и не
|
||||
застейджено. Миграции пака: **00019–00024**.
|
||||
|
||||
### Что построено и зачем
|
||||
|
||||
1. **Долг на материализацию — колонка с арендой** (`books.read_model_owed_at`, 00019).
|
||||
Ставится в ТОЙ ЖЕ транзакции, что закрывает границу работы (конец разбора и все три конца
|
||||
прогона), гасится сверкой метки, берётся в аренду тем, кто платит. **Зачем:** очередь жила в
|
||||
памяти процесса — упавший `Refresh` её терял, рестарт демона терял её всю, и оплаченный текст не
|
||||
доезжал до читателя НИКОГДА. Плюс «в покое» для потока теперь значит «и материализация не
|
||||
должна» — это Ф-56: книга, только что нарезанная, получала `end` и 204 «не переподключайся»
|
||||
ровно в те секунды, пока дерево ещё не легло.
|
||||
**Почему отошёл от прежней реализации:** удалены очередь в памяти (`runs.deferRefresh`/
|
||||
`DrainRefresh`/`Reader`) и бэкстоп «дерева нет вовсе» (`books.materializeMissingTrees`,
|
||||
`pgstore.BooksWithNoTree`) — оба покрывались одним механизмом, а бэкстоп к тому же не видел
|
||||
книгу, у которой дерево есть, но на границу старше. Дрейн переехал в `readmodel.Drain` — в
|
||||
пакет, чья это работа.
|
||||
2. **«Глава сделана» = последний проход, который книга на ЭТОМ деплое реально получает**
|
||||
(`books.edit_wave`, 00024). **Зачем:** счёт шёл по волне `edit`, и на деплое без редактора ни
|
||||
одна глава не была сделана никогда — шкала покупки не подрезалась, сервис бесконечно предлагал
|
||||
купить уже переведённое. Форму объявляет сам движок (пайплайн без редактора даёт волне `edit`
|
||||
знаменатель ноль). **Почему на КНИГЕ, а не на прогоне:** прочитанная с последнего прогона, она
|
||||
роняла `chapters_done` в ноль в момент допуска нового прогона — счётчик ходил назад, что канон
|
||||
запрещает. Флаг монотонный: книга, прошедшая редактирующий пайплайн, остаётся такой.
|
||||
3. **У клейма идемпотентности появился владелец** (`claim_token`, 00020) и **тождество интейка
|
||||
решается дайджестом файла** (`content_sha256`, 00023). **Зачем:** строка ключа не несла, какая
|
||||
попытка её держит — застрявшая попытка удаляла строку преемника (пользователь получал вторую
|
||||
книгу) или писала свою квитанцию поверх чужой (клиент реплеил `Location` несуществующей книги);
|
||||
а тождество по `Content-Length` не различало две РАЗНЫЕ книги с одинаковым объявлением.
|
||||
4. **Остановка АККАУНТА читается с аккаунта**, а не с потолка одного прогона; `resume` различает
|
||||
«прогон закончен, нужен новый» (`ceiling_reached`) и «пополните счёт» (`credit_unavailable`);
|
||||
`blocked` называет холд, который реально освободит шкалу. **Зачем:** всё это — «у вас нет
|
||||
денег» пользователю, у которого деньги есть, и отправка его делать не то.
|
||||
5. **Счётчик замечаний вернулся на главу** (`chapters.note_count`, 00022), `chapters.units_done`
|
||||
снесён (00021). **Почему это разворот решения 00016 того же пака:** 00016 снёс колонку с
|
||||
доводом «её никто не пишет» — здесь она возвращается ВМЕСТЕ с писателем, которого ей не
|
||||
хватало. Основание замерено: джойн, которым её заменяли, стоил 83% времени страницы библиотеки
|
||||
(16.8 мс против 3.4 мс на корпусе 40 книг × 500 глав, ВАКУУМИРОВАННОМ); со счётчиком — 6.6 мс.
|
||||
⚠ Условие замера названо здесь и при каждом другом числе про него (P8-FIX, PD-336): на ХОЛОДНОМ
|
||||
корпусе тот же джойн даёт 96%, и это не противоречие, а другой замер. Нормативный носитель
|
||||
вакуумированных цифр — миграция 00022, остальные на неё указывают. Бенчмарк оставлен
|
||||
в дереве: `go test ./internal/pgstore/ -bench LibraryPage -run xxx`.
|
||||
|
||||
### Что форсировано и почему
|
||||
|
||||
- **Работа по канону 0.4.0 лежит в дереве до его ратификации.** Основание: ответ контрактной
|
||||
сессии на записку зоны плюс слово владельца 20.08 про «кончились деньги → сообщить и дать
|
||||
продолжить». Ломающая правка ровно одна — `Run.stop_requested` (0.3.0 такого поля не объявляет);
|
||||
`cause.code: credit_unavailable` легален и по 0.3.0, второй уровень там открыт. Зависимость
|
||||
записана строкой **PD-327** и закрывается лендингом канона, а не работой зоны. Если форма
|
||||
сдвинется словом владельца — правки отделимы, карта в `archive/P7_ACT5_FIX_PLAN_2026-08-20.md` §5б.
|
||||
- **Тесты на АТОМАРНОСТЬ сверяют `xmin`** (транзакцию, последней писавшей строку). Это не
|
||||
экзотика ради экзотики: два пина акта проходили под мутацией «вынести метку из транзакции во
|
||||
второй оператор» — конечное состояние она не меняет, меняет окно, и утверждать про окно иначе
|
||||
тест не может.
|
||||
|
||||
### Что осталось, названо и не спрятано
|
||||
|
||||
- **PD-327** — ждёт лендинга канона 0.4.0 (не работа зоны).
|
||||
- **PD-297** — round-trip на строку в `SaveStructure` под эксклюзивной блокировкой книги. Не взят
|
||||
осознанно: путь самый опасный на запись, инструмент штатный (`tx.SendBatch`), но правку надо
|
||||
мерить на корпусной книге до и после.
|
||||
- **Четыре оси не смотрел никто и никогда:** деньги и леджер целиком · вход/сессии/CSRF · очередь
|
||||
и джобы · метрики. Пак их не менял, а все приёмки смотрели дифф. Это отдельный пак.
|
||||
- **Живого перевода на стенде не было ни разу** — движку нужны ключи провайдера и деньги. Всё
|
||||
после «прогон стартовал и остановился» (подпись банка, halt на потолке, резюм после пополнения)
|
||||
покрыто батареей против живого Postgres, а не живым движком.
|
||||
|
||||
### Как это проверялось
|
||||
|
||||
`make check` с гейтами (скипов 0) · каждая правка проверена ПОСАДКОЙ МУТАЦИИ: код испорчен именно
|
||||
так, как называет комментарий пина, пин обязан упасть, код возвращён (40 проб, все поймались) ·
|
||||
живые пробы по ручкам на стенде: интейк и реплей, `409 key_reused` на другой файл под тем же
|
||||
ключом, часть после файла на реплее, `/chapters` `/units` `/notes` `/bank`, ETag→304 и разные теги
|
||||
у страниц, 410 на чужой id главы, `after_version` из прошлой жизни, `unsupported_pair`, `/usage`
|
||||
на пустом счету и после `adjust`, старт и стоп прогона со `stop_requested` на проводе, SSE в
|
||||
покое и SSE с непогашенным долгом (Ф-56 на проводе), 400 вместо 413 на длинном теле.
|
||||
|
||||
### Самопроверка
|
||||
|
||||
Акт закрыт адверсариальным ревью СОБСТВЕННОГО диффа (9 линз × 2 рефутера + критик полноты, 84
|
||||
агента, 0 ошибок): **37 находок, 18 пережили рефутеров**, и почти все — дефекты, внесённые
|
||||
правками самого акта. Строки **PD-315…PD-327**, каждая с механизмом и пином. Два из них —
|
||||
собственные пины акта, проходившие под мутацией, которую сами называют (PD-318): это третье
|
||||
появление класса в паке, и он назван в плане §3 п.2 ещё до того, как повторился.
|
||||
|
||||
- **P7, акт 5 (начало) — исполнение фиксов по ревью акта 4 + ответ контрактной сессии.**
|
||||
План и рабочий список — **`platform/docs/archive/P7_ACT5_FIX_PLAN_2026-08-20.md`** (читать первым; §5а — что пришло из
|
||||
контракта).
|
||||
⚠ **Контрактная сессия ответила 20.08 на записку зоны `archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md`: канон стал
|
||||
0.4.0** (лежит в дереве контрактной зоны, НЕ ратифицирован — до лендинга в силе 0.3.0). Все три
|
||||
вопроса, висевшие на владельце, закрыты. Зоне пришла работа: `resume` исчерпанного прогона — 409 с
|
||||
двумя РАЗНЫМИ причинами (`ceiling_reached` против нового `credit_unavailable`); «глава сделана» =
|
||||
последний реально пройденный проход, и это ДЕНЬГИ — `ChaptersLeft` на деплое без волны редактора не
|
||||
подрезается вовсе, сервис бесконечно предлагает купить уже переведённое; отпечаток интейка — по
|
||||
содержимому, `Content-Length` из нормы вышел, а невозможность установить тождество обязана давать
|
||||
409, а не реплей; `Run.stop_requested` обязательным булевым (единственная ломающая правка).
|
||||
Отклонены: слово под суточный потолок движка (решение владельца 15.08) и предложенный зоной признак
|
||||
«прогон продолжаем» (три лишних чтения на карточке, которую перечитывают на каждый кадр).
|
||||
Акт 4 закрылся адверсариальным ревью СОБСТВЕННОГО диффа (9 линз × 2 рефутера, 75 агентов):
|
||||
**33 находки, 27 пережили рефутеров**, и часть из них внесли сами правки акта 4. Тяжелейшая —
|
||||
четыре независимые линзы сошлись на том, что **реконсилятор снимал стоп подписи банка, которого
|
||||
никто не подписывал**, и доспавнивал движок без `--verify-bank`: складывая снятие стопа внутрь
|
||||
общего `reopen`, акт 4 не проверил, что `reopen` зовёт не только `resume`, но и свип. Нарушение
|
||||
согласия пользователя; починено ключом на вызывающего, пин проверен посадкой мутации.
|
||||
⚠ **Первый написанный на это пин ПРОШЁЛ под мутацией** — фикстура не доходила до кода, потому что
|
||||
часы сервиса захвачены при конструировании и `f.now` их не двигает. Урок в плане, §3.
|
||||
Закрыто также: загрузочная проверка пар — на ДОСТУПНОЙ половине (деплой с одними `:unavailable`
|
||||
больше не стартует), интейк не трактует пустой список как «пускать всё», теги сравниваются
|
||||
регистронезависимо. Батарея: 18 пакетов, exit 0, скипов 0, тестов **503**.
|
||||
|
||||
- **P7, акт 4 — доработка 20.08: сверка ВСЕХ находок против дерева + триаж §8.2.**
|
||||
(Пакет релеев `archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md` §7; три вопроса владельцу §8.1а; вопросы контракта
|
||||
вынесены в `platform/docs/archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md` для отдельной контрактной сессии.)
|
||||
Все 74 позиции обеих приёмок сверены поштучно с ТЕКУЩИМ деревом — каждая с цитатой живого кода,
|
||||
исход «не проверено» отдельно от «опровергнуто».
|
||||
⚠ **Три места числились закрытыми и закрыты не были — правка не закрывала собственный сценарий:**
|
||||
(1) подрезка буфера потока сверялась ПОСЛЕ того, как водяной знак перепрыгнул дыру, поэтому
|
||||
условие не срабатывало никогда и кадры `note` терялись молча (PD-283); (2) `CompleteIdempotency`
|
||||
писала квитанцию в строку, которая попытке уже не принадлежит — воспроизведено живым PG, клиент
|
||||
получал квитанцию ЧУЖОГО запроса (PD-284); (3) пин на снятие `--verify-bank` был ПУСТЫМ: фикстура
|
||||
стартовала прогон без `stop_for_signing` (PD-285). Все три «подтверждались» зелёной батареей.
|
||||
**Ещё закрыто:** HEAD на потоке больше не держит горутину и ошибка `Flush` не выдаётся за
|
||||
отправленный кадр (PD-286) · `blocked` называет чужую книгу только когда её холд укорачивает шкалу
|
||||
(PD-287, пина не было вовсе) · `note_count` и `GET /notes` описывают одно множество (PD-288) ·
|
||||
устаревшая доставка не рождает кадр (PD-289) · чтения на одном снапшоте (PD-290) · флаг «текст
|
||||
прочитан» переехал на ПАРУ (PD-291) · снятие стопа банка внутри транзакции переоткрытия, отдельного
|
||||
писателя больше нет (PD-292) · проход материализации подрезается своим бюджетом (PD-293) ·
|
||||
блокировка книги в решениях банка (PD-294) · дубликат индекса снят (PD-295) · маршруты и коды
|
||||
ошибок — по ОДНОЙ таблице, тесты ходят по ней (PD-296) · бэкстоп-свип для книги без дерева
|
||||
(PD-276 дозакрыт) · интейк не платит за третью ре-нарезку (PD-248 актуализирован).
|
||||
**Триаж §8.2:** 17 сделано, 5 отклонено с причиной, 1 релей. Батарея: 18 пакетов, exit 0, скипов 0,
|
||||
линтер 0 issues, тестов **458 → 501**; каждая правка проверена посадкой мутации, не рассуждением.
|
||||
⚠ **Осознанно НЕ сделано, с причиной:** PD-297 (round-trip на строку под блокировкой книги —
|
||||
мерить прежде правки) · PD-202 (draft-only, вопрос владельцу) · PD-282 (канон §resumeRun не
|
||||
покрывает «денег не осталось») · PD-298 (сперва сверка у движка) · PD-299.
|
||||
**Осей, которых не смотрел никто:** деньги и леджер · вход/сессии/CSRF · очередь и джобы · метрики.
|
||||
|
||||
- **P7: приёмка → правки → приёмка правок (17.08).**
|
||||
⚠ Полный хендофф под сессию без контекста — **`platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md`** (читать §0–§3,
|
||||
дальше по нужде). Там же: принципы приёмки, сверка с исходным промтом, техдолг, план следующей сессии.
|
||||
Категорийное ревью по 12 осям с адверсариальной верификацией: 80 находок, **54 подтверждено, 16
|
||||
правдоподобно, 10 опровергнуто**; после дедупа **46 уникальных дефектов** — 3 блокирующих класса,
|
||||
15 HIGH, 17 MEDIUM. Батарея была зелёная, потому что все три блокирующих дефекта живут в путях,
|
||||
которые тесты проходили только в безопасной форме.
|
||||
**Починено в зоне** (регистр PD-257…PD-281): пере-нарезка книги больше не валит читающую модель
|
||||
навсегда (отложенный констрейнт, миграция 00018) · упавший `tmctl export` больше не затирает текст
|
||||
пустотой · весь блок идемпотентности (гонка первых попыток 500→409, область ключа, отпечаток с
|
||||
размером файла, закрытие ключа вне контекста запроса) · база полосы прогресса считает тот же
|
||||
проход, что числитель · SSE (подрезанные кадры → `resync_required`, дедлайн записи, id из
|
||||
будущего) · материализация не голодает свип · банк читается на штатном деплое · деплой-нота больше
|
||||
не включает обратно грант, который PD-104 выключил. Батарея: 18 пакетов, exit 0, скипов 0,
|
||||
линтер 0 issues, тестов **458 → 490**.
|
||||
⚠ **Приёмка ПРАВОК (9 осей + fable-5) нашла, что восемь дефектов внесли сами правки** — батарея не
|
||||
увидела ни одного, три были запинены под ошибку автора. Все исправлены; разбор — хендофф §0.
|
||||
**Б-1 ПОЧИНЕН разбором с владельцем 17.08 (PD-277).** Рамка приёмки была неверна: это не «нужен
|
||||
новый канал в движок», а невыполненная половина строки единого бэклога **191(в)** (слово владельца
|
||||
16.08, D39.144) — «проводка `resume = снятие стопа` до движка». Движок при выключенном
|
||||
`--verify-bank` уже делает модель владельца: неподписанное едет авто-строками с пометкой
|
||||
⟨проверить⟩ (`mining.go:211-231`, D39.42 п.3), а платформа передавала флаг обратно и роняла прогон
|
||||
в тот же стоп. Снято: `l.VerifyBank && !l.BankReleased`; пин `TestAResumedRunIsSpawnedWithoutTheSigningStop`.
|
||||
⚠ Остаток: `decline` пользователя до движка не доезжает (движок читает `mined_rejects`) — строка
|
||||
бэклога **192**, отложена владельцем.
|
||||
**PD-273 закрыт решением владельца 17.08:** статус остаётся `awaiting_bank`, лечение — признак
|
||||
«стоп был запрошен» в контракте; готовая строка для единого бэклога — хендофф §7(и), вставляет
|
||||
оркестратор (зона `docs/` не наша).
|
||||
**Разбор, план, обязательства через оркестратора и команды воспроизведения —
|
||||
`platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md`.**
|
||||
⚠ Заявки раздела «Сессия P7» ниже приёмку НЕ прошли — читать их вместе с хендоффом, не вместо.
|
||||
|
||||
- **P7 ПОСТРОЕН — заявка САМОЙ сессии написания (17.08).** ⚠ **Приёмку эта заявка НЕ прошла** — см.
|
||||
верхнюю запись и `archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md`: 46 уникальных дефектов, три блокирующих класса, из них
|
||||
ни один батарея не видела. Читать ниже как исходную позицию, а не как состояние дерева.
|
||||
Читающая поверхность контракта **0.3.0**:
|
||||
главы · пары с текстом · замечания · банк и подпись · SSE на КНИГЕ · `GET /capabilities` ·
|
||||
машинная модель ошибок (`code` + `request_id`) · условные чтения и сжатие · `Idempotency-Key` ·
|
||||
`structure_version`. Плюс форварды (`title`, `reject_reason`), PD-104 (грант → 0), демонтаж
|
||||
построенного гейта полноты банка (D39.144), PD-241, конформность построенного по §5 промта и
|
||||
рантбук деплоя, прогнанный ЖИВЫМ `migrate`. Батарея на стенде `~/.local/pgsql` (порт 55433) под
|
||||
`-race` с ОБОИМИ гейтами: **EXIT=0, 18 пакетов, скипов 0, линтер 0 issues**, тестовых функций
|
||||
**416 → 458**. Дифф: 46 файлов, +2329/−646. Селф-ревью финалом — ТРИ независимых ревьюера, два из
|
||||
них другой модели: **22 находки, 17 подтверждены и починены** (две HIGH нашли оба контрактных
|
||||
ревьюера независимо), 5 — диспозиция с аргументом. Разделы ниже — записка-план, живые пробы,
|
||||
таблица селф-ревью, obstacle, вопросы оркестратору.
|
||||
|
||||
|
|
|
|||
472
platform/docs/archive/platform-PROGRESS-P8.md
Normal file
472
platform/docs/archive/platform-PROGRESS-P8.md
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
# Слайс зонного журнала: эра пака P8-FIX (21–22.08.2026)
|
||||
|
||||
> ⚠ **Архив. Инструкции отсюда НЕ исполнять, при онбординге НЕ читать** — только по конкретной ссылке.
|
||||
> Вынесено оркестратором №18 22.08 при актуализации доков. Состояние зоны — шапка живого
|
||||
> `../platform-PROGRESS.md`; дефекты — `../DEFECT_REGISTER.md`; правила, пережившие пак, —
|
||||
> `../STACK_DECISIONS.md`; ратификация — **D39.154** в `docs/architecture/05-decisions-log.md`.
|
||||
>
|
||||
> **Что этот пак сделал:** закрыл фикс-лист приёмки P7 (пункты 1–7 и 9–13) и врезанный первым
|
||||
> блокер свипа `PD-169` — голодание, из-за которого расчёт денег вставал для ВСЕЙ инсталляции.
|
||||
> Плюс спил пер-термного write-пути банка по слову владельца (`PD-370`, ратификация D39.144).
|
||||
> Лендинг — `31f1f82`.
|
||||
>
|
||||
> ⚠ **Что из этого отчёта ПЕРЕНЕСЕНО в долговечные носители и там первично:**
|
||||
> инвентарь каналов шва с движком → `../STACK_DECISIONS.md` §«Инвентарь каналов движка» (и там он
|
||||
> ПОЛНЕЕ: дописан канал текста `tmctl export --json --pairs`, которого в этой таблице не было) ·
|
||||
> развилка владения `book.yaml` с четырьмя вариантами → строка **199(а)** единого бэклога ·
|
||||
> поимённый разбор канона под минор 0.5.0 → строка **206** · граница sqlc (41 запрос) → `BACKLOG.md`
|
||||
> **П-19** · ловушка `.check.log` и третий гейт батареи → `PD-374`.
|
||||
|
||||
- **P8-FIX — ФИКС-ЛИСТ ПРИЁМКИ P7 ЗАКРЫТ. Дерево готово к приёмке и лендингу (22.08).**
|
||||
Батарея: 18 пакетов, exit 0, **скипов 0**, линтер 0 issues, скипов 0, тестов **561** (HEAD 532). Регистр —
|
||||
**370** строк, открытых **68** (14 minor, 53 info, **1 major**) — сверено командой на момент сдачи
|
||||
(`python3 docs/scripts/counts.py --check`, exit 0; `--lint` exit 0; ⚠ вес регистра этим скриптом НЕ
|
||||
проверяется — числа сняты его печатью, а не гейтом). **Открытый major — `PD-370`**, контрактная
|
||||
половина спила пер-термной подписи банка: она не работа зоны, закрывать её здесь было бы подгонкой
|
||||
под критерий приёмки. Миграция пака: **00025**. Дерево не закоммичено и не застейджено.
|
||||
**Удалённых имён тестов ТРИ, и все три — по решению владельца 22.08** (сверено исполнением, не
|
||||
памятью: имена из `git show HEAD:<файл>` по всем `_test.go` против дерева):
|
||||
`TestABadDecisionRefusesTheWholeBatchAndNamesTheItem` · `TestAnUnknownTermRefusesTheBatch` ·
|
||||
`TestTheBankIsReplacedWhileTheDecisionsSurvive` — они описывали пер-термную подпись банка, которую
|
||||
`D39.144` отменил, а этот пак снял из кода (`PD-370`). Тест снятого кода — не потеря покрытия;
|
||||
третий переписан на СОХРАНИВШЕЕСЯ свойство (`TestTheBankIsReplacedFromTheEnginesReadOut`).
|
||||
Плюс один ПЕРЕИМЕНОВАН по факту изменения смысла (`…GivesTheHoldBackOnlyWhenAsked` →
|
||||
`…AlwaysGivesTheHoldBack`, `PD-361`) — имя заведено этим же паком.
|
||||
⚠ **Пинг оркестратору №1 — ЕГО зона, сверить при лендинге:** `docs/architecture/05-decisions-log.md:542`
|
||||
держит якорь на `internal/pgstore/perf_test.go:13` с токеном «96%». Пак ЭТОТ комментарий правил
|
||||
(`PD-366`: он лгал про свой же бенчмарк), токен я сохранил намеренно и `--lint` зелёный — но док
|
||||
чужой, поэтому говорю, а не считаю вопрос закрытым.
|
||||
⚠ **Пинг оркестратору №2 — попутная находка ВНЕ зоны пака:** `PD-369` / `П-21` — `ClaimIdempotency`
|
||||
под конкуренцией отвечает 500 (1–2 падения на ~80 прогонов собственного теста). Файл в диффе пака
|
||||
не участвует, не трогал.
|
||||
⚠ **Пинг оркестратору №3 (`PD-370`, требует контрактной сессии):** канон 0.4.0 несёт пер-термный
|
||||
`BankDecision{action: approve|decline}`, а `D39.144` эту модель отменил 16.08 — подписывается весь
|
||||
банк одним ОК. Зона канон исполняет, снять в одностороннем порядке не может. Правка канона →
|
||||
правка зоны, в этом порядке.
|
||||
⚠ **Сдача идёт ПОСЛЕ второй волны ревью**, которая нашла в самом паке блокер (`PD-353`) и два
|
||||
дефекта, делавших его собственные механизмы недостижимыми (`PD-354`, `PD-355`) — раздел «Волна 2».
|
||||
|
||||
### Записка-план по §4 промта: пункт → где исполнено / отказ с причиной
|
||||
|
||||
| Пункт | Исход | Где |
|
||||
|---|---|---|
|
||||
| **Блокер свипа** (врезан первым) | **исполнен четырьмя механизмами** | ниже, отдельным разделом; `PD-169` пере-открыт и закрыт заново |
|
||||
| 1. `ContractVersion` 0.3.0 при формах 0.4.0 | исполнен | `internal/httpapi/capabilities.go` + гейт `internal/gates/contract_test.go` (`PD-328`) |
|
||||
| 2. Пины на два поля `EventBase` | исполнен, посадка сперва воспроизведена | `internal/pgstore/events_test.go` (`PD-329`) |
|
||||
| 3. Бюджет попыток материализации | исполнен | `internal/readmodel/readmodel.go`, миграция 00025 (`PD-330`) |
|
||||
| 4. sqlc + гейт разбора SQL | **гейт построен; sqlc — граница названа, решение владельца** | `internal/pgstore/sqlgate_test.go`; разбор — `PD-44`, строка бэклога **П-19** |
|
||||
| 5. `PD-297` (round-trip под блокировкой) | **отказ с причиной** | нечем и не на чем мерить — ниже |
|
||||
| 6. Труба доставки решений банка (строка 199а) | **не строил, сдаю вопросом** — как промт и требует | ниже, отдельным разделом |
|
||||
| 7. Мусор: задвоенная строка доккомментария | исполнен | `internal/books/books.go` `canTranslate` (`PD-335`) |
|
||||
| 8. Ревью-пак четырёх осей | **НЕ мой** — заказ пака P8-REVIEW | — |
|
||||
| 9. Один носитель на факт для замера 00016→00022 | исполнен, с названным расхождением с буквой пинга | `internal/pgstore/perf_test.go` (`PD-336`) |
|
||||
| 10. Пол на пустой манифест у материализатора | исполнен | `internal/ingest/manifest.go` `Whole()`, `internal/readmodel/readmodel.go` (`PD-332`) |
|
||||
| 11. `PD-327` стоит open при ратифицированном каноне | исполнен | регистр, `fixed(P8-FIX)` |
|
||||
| 12. Изоляция читающих чтений не запинена | исполнен, посадка сперва воспроизведена | `internal/pgstore/isolation_test.go` (`PD-333`) |
|
||||
| 13. `CreditHeldBy`: оговорка не запинена | исполнен, посадка сперва воспроизведена | `internal/pgstore/credits_test.go` (`PD-334`) |
|
||||
| `PD-255` (норма комментариев сменилась) | закрыт решением владельца 21.08 | регистр |
|
||||
|
||||
### Блокер свипа: почему лечение — не «поднять константу»
|
||||
|
||||
Диагноз пинга подтверждён построчно и **воспроизведён исполнением**. Живое свойство: проход 2 мин
|
||||
против 60 с на прогон ⇒ двух медленных прогонов хватает, чтобы съесть проход целиком, после чего
|
||||
`ctx.Err()` обрывает цикл и `UnsettledRuns` — единственный ретрай отложенного расчёта — не
|
||||
вызывается ВООБЩЕ, для всей инсталляции, каждый проход. Построено:
|
||||
|
||||
1. **Проход разделён на фазы.** Реконсиляция получает половину остатка, расчёт — свой кусок
|
||||
(`runs.phaseBudget`). Выбранный бюджет фазы больше не ошибка свипа: очередь просто ждёт тика.
|
||||
2. **Отсрочка с растущим бэкоффом на ПОПЫТКЕ** (`run_attempts.reconcile_after`, миграция 00025).
|
||||
Свип читает `RunsToReconcile`, а не `ListLiveRuns`; порядок «старейший первым» не тронут — он
|
||||
верен для всего, что просто медленно, — а из ГОЛОВЫ заклиненный прогон убирает именно отсрочка.
|
||||
Телеметрия (`Lag`) по-прежнему видит всё.
|
||||
3. **Терминальное состояние после N неудач** (`runs.StalledAfter` = 5, ≈ четверть часа попыток):
|
||||
прогон считается ЗАСТРЯВШИМ, гейдж `tm_platform_runs_stalled`, одна ERROR-строка на пересечении
|
||||
порога — не на каждом тике.
|
||||
4. **Ручка у ОПЕРАТОРА, которой не было вовсе:** `tmplatformctl runs [--stalled]` называет строку,
|
||||
число неудач, следующую попытку, сумму холда и его возраст, юнит и последнюю ошибку;
|
||||
`tmplatformctl run abandon --run <id> --reason <text> [--release-hold]` — терминальный вердикт.
|
||||
Плюс `books --abandoned` и `book refresh` для второй половины (материализация).
|
||||
|
||||
**Границу «дальше не иду» называю явно:** автоматически решать про деньги прогона, о котором нельзя
|
||||
спросить, этот пак НЕ стал. Правило реконсилятора везде — «я не смог спросить» ≠ «прогона нет»;
|
||||
прогон, упавший N раз, это ровно прогон, о котором никто не смог спросить, и счётчик перед той же
|
||||
ошибкой её не чинит. Автоматический ответ на этот вопрос и есть эскроу (строка 136, П-18), который
|
||||
промт §4.6 из пака исключает. Что счётчик покупает — что человека ИЗВЕЩАЮТ; что покупает ручка —
|
||||
что извещение чего-то стоит. Хвост записан строкой **П-20**, а не подразумевается.
|
||||
|
||||
Оба числа стали ручками: `TM_PLATFORM_SWEEP_BUDGET` и `TM_PLATFORM_RUN_BUDGET`. Второе —
|
||||
отдельная находка пака: поле `runs.Config.RunBudget` было объявлено, задокументировано и **никем не
|
||||
присваивалось** (`PD-331`), то есть ручка существовала в структуре и не делала ничего.
|
||||
|
||||
### Пункт 4: что построено и где проходит граница sqlc
|
||||
|
||||
**Построен гейт, который доходит туда, куда sqlc не дойдёт:** `TestEverySQLStatementParsesAgainstTheMigratedSchema`
|
||||
сворачивает КАЖДЫЙ SQL пакета прямо из исходника (литералы, конкатенации, именованные константы,
|
||||
плюс оба внутренних помощника, принимающих SQL параметром) и планирует его живым Postgres'ом
|
||||
(`explain (generic_plan)`) против мигрированной схемы. **162 оператора, все планируются.**
|
||||
Несворачиваемый SQL — ОШИБКА гейта, а не пропуск: единственное исключение выписано таблицей в самом
|
||||
гейте (`store.go` `Ready`, где имя таблицы принадлежит goose). Тем же гейтом закрыт вопрос
|
||||
фикс-листа «есть ли в read-модели запрос, которого не касается ни один тест»: теперь его касаются
|
||||
все, на каждом прогоне батареи.
|
||||
|
||||
**Граница sqlc — мой выбор, аргумент:** склеек в пакете 25 из 147, фрагментов-констант 15, у
|
||||
`lastRun` девять потребителей — read-модель для sqlc недостижима по построению, и это ровно та
|
||||
часть, где рантайм-ошибки и случались. Свободных от склейки файлов целиком пять: `credits.go`(15) ·
|
||||
`identity.go`(13) · `idempotency.go`(7) · `sessions.go`(5) · `observe.go`(1) = **41 запрос**.
|
||||
Это единственный кусок, где инструмент силён; конверсия — отдельный пак (одиннадцать денежных
|
||||
запросов идут внутри чужой транзакции, генерённый код коммитится, нужен пин версии и гейт
|
||||
«сгенерённое актуально»). Взять его здесь на две-три ручки — ровно то, от чего предостерёг
|
||||
фикс-лист: купить инструмент туда, где не болит. Заведено строкой **П-19**.
|
||||
⚠ **Вопрос закрыт владельцем 22.08: sqlc берётся ОТДЕЛЬНОЙ СЕССИЕЙ, не внутри пака** — изменений
|
||||
много и мешать их с содержательной работой нельзя. Граница остаётся названной: 41 запрос.
|
||||
|
||||
### Пункт 5, `PD-297`: отказ с причиной
|
||||
|
||||
Мерить нечем и не на чем. Сама строка регистра объявляет замер на здешнем стенде НЕпредставительным
|
||||
(unix-сокет против управляемого PG по TCP — разница на два порядка), а корпусной книги нет: она
|
||||
появляется на холодном прогоне движка, которым гейчена строка 202 (решение владельца 20.08). Правка
|
||||
самого опасного на запись пути без замера — ровно то, что эта строка запрещает. Берётся вместе с
|
||||
холодным прогоном.
|
||||
|
||||
### Пункт 9: расхождение с буквой пинга, названное вслух
|
||||
|
||||
Пинг просил свести условия В миграцию 00022. **Она РЕЛИЗНАЯ**, и гейт неизменности
|
||||
(`migrations.sha256` + `TestReleasedMigrationsAreUnchanged`) её правку запрещает; правка гейта ради
|
||||
этого была бы подгонкой под зелень, что промт и `CLAUDE.md` запрещают прямо. Поэтому 00022 остаётся
|
||||
нормативным носителем вакуумированных цифр как есть, а условия дописаны в носители, которые править
|
||||
законно. «96%» не удалено и получило своё условие: это ХОЛОДНЫЙ корпус акта 4 (636 мс против 24),
|
||||
тогда как 80–83% — вакуумированный. Носители мерили разное и все честны.
|
||||
|
||||
### Строка 199(а): развилку НЕ решал — сдаю вопросом, как требует §4.2 промта
|
||||
|
||||
⚠ **ПОПРАВКА ВЛАДЕЛЬЦА 22.08, и она сносит посылку, на которой я строил разбор.** Я описывал трубу
|
||||
как доставку ПЕР-ТЕРМНЫХ решений («approve/decline на термин»). **Такой модели нет и не было
|
||||
с 16.08:** D39.144 ратифицировал ОДИН ОК над ВСЕМ банком — «пер-термная подпись = сотни кликов —
|
||||
НЕ модель продукта», «в модели одного ОК сценария „какие 17 из 300 решены“ не существует».
|
||||
Пер-термно существует не подпись, а **правка**: поправить/добавить термин до подписи, и — после
|
||||
прочтения — поправить термин с пере-генерацией задетых глав (вторая половина гейчена строкой 192).
|
||||
|
||||
Труба от этого не отменяется, но её ГРУЗ другой: не «решения», а **правки терминов** (`src` → новый
|
||||
`dst`, плюс добавленные) в `mined_delta`. Развилка ниже (кто объявляет ключи и кто создаёт файлы)
|
||||
от этого не меняется ни в одном пункте — она про владение `book.yaml`, а не про груз.
|
||||
|
||||
⚠ **Модель, которую D39.144 отменил, была у зоны ПОСТРОЕНА — 22.08 СНЯТА по слову владельца**
|
||||
(`PD-370`). Что ушло: маршрут `POST /books/{bookId}/bank/decisions`, хендлер и wire-типы, метод
|
||||
интерфейса `Library`, `SubmitBankDecisions`, типы `BankDecision`/`BankReceipt`, `UnknownTermError`
|
||||
и их пины. На месте write-пути — пометка для будущей сессии (`internal/httpapi/reading.go`, блок
|
||||
«THE WRITE HALF OF THE BANK IS GONE»): что отменено, что ратификация ОСТАВИЛА пер-термным
|
||||
(правку), и что под неё уже есть в схеме. ⚠ **Контрактная половина остаётся открытой** — канон
|
||||
0.4.0 несёт путь, три схемы и глагол, и это зона контрактной сессии.
|
||||
|
||||
Посылку промта пере-проверил САМ, чтением кода движка, а не по докам. Подтверждается вся:
|
||||
|
||||
| Факт | Улика |
|
||||
|---|---|
|
||||
| движок читает `mined_delta`, только если путь объявлен | `backend/internal/pipeline/mining.go` `loadMinedDelta`: `if r.Book.MinedDelta == "" { return nil, nil }` — ни одного чтения |
|
||||
| то же для `mined_rejects` | там же, `loadMinedRejects` |
|
||||
| объявленный ключ с НЕсуществующим файлом валит загрузку конфига целиком | `backend/internal/config/book.go:199-207`: `bad("mined_delta %s is not readable: %v")` |
|
||||
| ключей нет в шаблоне оператора | `backend/example/book.yaml` — ноль вхождений `mined_delta`/`mined_rejects` |
|
||||
| платформа пишет `book.yaml` ОДИН раз и фиксированным набором из пяти ключей | `platform/internal/books/render.go` — `O_EXCL`, «writes the file once and never again»; ключи `book_id · title · source_lang · target_lang · source_file` |
|
||||
| **новое, чего в промте не было:** декодер движка СТРОГИЙ — незнакомый ключ это жёсткая ошибка | `backend/internal/config/book.go:142-144`, `dec.KnownFields(true)` — то есть платформа не может «на всякий случай» дописать ключ, которого сборка движка не знает |
|
||||
| относительные пути резолвятся от каталога КНИГИ | `backend/internal/config/book.go:148-162`, `resolve()` — значит один шаблон на все книги даёт пер-книжные пути без подстановки |
|
||||
| форматы файлов | `mined_delta` = `seed.File` (`terms:` со схемой `internal/seed/seed.go:63-80`), грузится `membank.LoadGlossarySeed` и пере-штампуется `Source:"mined"`; `mined_rejects` = `rejects: [{src, note}]` (`mining.go` `minedRejectFile`), это ФИЛЬТР ПРЕДЛОЖЕНИЙ, в банк не входит |
|
||||
| на стороне платформы решения write-only | `internal/pgstore/readmodel.go` `SubmitBankDecisions` пишет `action`/`dst`; ни один SELECT их не читает, воркер описан комментарием собственной миграции `00002_readmodel.sql:172-174` и не построен |
|
||||
|
||||
**Варианты с ценой (решает владелец, не я):**
|
||||
|
||||
- **A. Платформа объявляет два ключа при рождении книги и сразу создаёт два пустых файла.**
|
||||
Работает без единой правки движка и без действий оператора; пустой файл законен (`terms:` пуст ⇒
|
||||
ноль записей, `os.Stat` проходит). **Цена:** платформа начинает знать два ключа СХЕМЫ движка, а
|
||||
форма Б ратифицирована ровно наоборот (D39.110 §2б: шаблон — деплой-артефакт оператора, платформа
|
||||
подставляет только личность, языки и имя файла). Плюс строгий декодер: сборка движка, которая
|
||||
этих ключей ещё/уже не знает, перестанет грузить КАЖДУЮ новую книгу.
|
||||
- **B. Ключи несёт шаблон оператора.** Платформа схемы не узнаёт, пути пер-книжные достаются даром.
|
||||
**Цена:** сам по себе вариант НЕ работает — движок отказывается грузить конфиг с объявленным, но
|
||||
отсутствующим файлом, а создавать файлы всё равно некому, кроме платформы. То есть B требует
|
||||
половины A или варианта D.
|
||||
- **C. Двигается ратифицированная граница владения:** платформе разрешено писать в каталог книги эти
|
||||
два файла (и только их), ключи несёт шаблон. **Цена:** правка ратификации D39.110; зато платформа
|
||||
по-прежнему не знает схемы — она пишет файлы по путям, которые прочитала из уже существующего
|
||||
`book.yaml`.
|
||||
- **D. Движок терпит объявленный, но отсутствующий `mined_delta`/`mined_rejects` как пустой.**
|
||||
Одна правка `backend/internal/config/book.go`, после чего работает чистый B: платформа пишет файлы
|
||||
только когда решения есть, и ничего не обязано существовать заранее. **Цена:** ослабляется
|
||||
намеренный гард — опечатка оператора в пути становится тихой. Смягчается тем, что послабление
|
||||
только для двух ключей и с громким WARN. Это строка ЕДИНОГО бэклога (движок), не моя.
|
||||
|
||||
**Рекомендация зоны: C или D+B.** Обе оставляют платформу в неведении о схеме движка — а это и есть
|
||||
то свойство, ради которого форма Б ратифицировалась. A решает быстрее всех и покупает это тем, что
|
||||
платформа впервые начинает знать схему чужой стороны, причём под строгим декодером.
|
||||
⚠ **Строить начну только после ратификации.** Границы, если разрешат, приняты как в §4.2: писать не
|
||||
«перед resume», а только на том резюме, которое СНИМАЕТ стоп подписи (`l.VerifyBank &&
|
||||
!l.BankReleased`); `--verify-bank` не трогать; пост-ридинговый цикл (строка 192) не проектировать.
|
||||
`PD-277` в регистре относил эту работу к отложенной 192. ⚠ **22.08 он ПЕРЕ-ДИСПОЗИРОВАН** — не по
|
||||
развилке, а по спилу глагола: остаток «честность `decline`» умер вместе с ним. Ниже — прежняя
|
||||
формулировка обязательства, оставленная как след решения: правлю его только после ратификации, как
|
||||
промт и велит.
|
||||
|
||||
### Как это проверялось
|
||||
|
||||
**Батарея:** `cd platform && make check` с ОБОИМИ гейтами (`TM_PLATFORM_TEST_DSN` на живом
|
||||
PostgreSQL 18.4, `TM_PLATFORM_TEST_ENGINE_BIN` + `_BOOK_TEMPLATE`) — 18 пакетов, EXIT=0, скипов 0,
|
||||
линтер «0 issues». Дифф имён тестов — исполнением: `grep -rho '^func Test[A-Za-z0-9_]*'` по дереву
|
||||
против `git show HEAD:<файл>` по всем `_test.go`. ⚠ **Замер пере-снят при сдаче** и его исход
|
||||
изменился: `comm -23` даёт ТРИ удалённых имени — все три описывали пер-термную подпись банка,
|
||||
снятую 22.08 по слову владельца (`PD-370`, разбор в шапке), — а `comm -13` даёт 29 новых.
|
||||
Прежняя формулировка «удалённых нет» была верна до спила и снята, а не оставлена рядом с новой.
|
||||
|
||||
**Посадки мутаций волны 1 — 20, поймано 20** (перечень ниже; счёт пере-снят при сдаче — прежняя
|
||||
цифра «16» не сходилась с собственным списком в той же фразе). Каждая ставилась в код, пин обязан был упасть, код возвращался:
|
||||
снятие `payload["revision"]` ✅ · снятие `payload["structure_version"]` ✅ · `ContractVersion` назад
|
||||
на 0.3.0 ✅ · `inReadTx` без обеих опций ✅ · без изоляции ✅ · без read-only ✅ · `ListBooks` на
|
||||
`inTx` ✅ · снятие `and book_id <> $2` ✅ · снятие пола самоописания манифеста ✅ · снятие пола «ноль
|
||||
глав» ✅ · обе фазы свипа на ОДНОМ дедлайне ✅ · ранний возврат фазы из `Sweep` ✅ · свип читает
|
||||
`ListLiveRuns` вместо списка «пора» ✅ · отсрочка только по ошибке ✅ · `DeferReadModelDebt` в
|
||||
`now()` ✅ · долг никогда не списывается ✅ · новая граница наследует потраченный бюджет ✅ · колонка,
|
||||
которой нет в схеме, в СКЛЕЕННОМ фрагменте (гейт SQL) ✅ · то же в локальной `const q` ✅ ·
|
||||
несворачиваемый SQL ✅.
|
||||
⚠ **Три посадки из этого списка сперва ПЕРЕЖИЛИ** мои же пины и вскрыли настоящие дефекты пака —
|
||||
разбор в `PD-337`/`PD-338`/`PD-339`; пины переписаны, мутации пере-посажены.
|
||||
|
||||
**Воспроизведение ПЕРЕД пином — там, где промт этого требовал (пп. 2, 12, 13):** три мутации
|
||||
посажены разом в HEAD-код и прогнана ПОЛНАЯ батарея — 18 пакетов, exit 0, скипов 0. То есть все три
|
||||
дыры подтверждены исполнением, а не прочитаны в пинге.
|
||||
|
||||
**Живые пробы — настоящий `tmctl`, живой Postgres, дев-демон на 127.0.0.1:8098:**
|
||||
- `GET /v0/capabilities` → `"contract_version": "0.4.0"` (пункт 1 на проводе);
|
||||
- `tmplatformctl seed` — аккаунт, грант, книга через ЖИВОЙ `POST /v0/books`: интейк прошёл, движок
|
||||
нарезал 3 главы, `GET /chapters` отдаёт 3, долг материализации погашен, `read_model_attempts = 0`.
|
||||
**Это отрицательная проверка нового пола: законный манифест живого движка он НЕ отвергает;**
|
||||
- **PD-162 воспроизведён живьём** — каталог книги унесён, прогон принят (202), спавн падает.
|
||||
`tmplatformctl runs` печатает строку с числом неудач `1`, временем следующей попытки, суммой холда
|
||||
`0.090000`, его возрастом, `(none)` вместо юнита и текстом ошибки движка. **До этого пака оператор
|
||||
видел только растущий счётчик `sweep_unfinished_total` и не мог сделать ничего;**
|
||||
- `run abandon` без флага: прогон `failed`/`service_error`, деньги не тронуты, книга РАЗМОРОЖЕНА —
|
||||
следующий прогон принят (202). С флагом `--release-hold`: холд вернулся целиком;
|
||||
- **деньги пере-считаны из сырого леджера двумя путями и сошлись до цента:** кэш
|
||||
`account_balances.balance_micro_usd` = 5000000, `sum(amount_micro_usd)` по `credit_ledger` =
|
||||
5000000, открытых холдов 0; строки леджера — `grant · hold · hold_release · hold · hold_release`;
|
||||
- метрики на проводе: `tm_platform_runs_stalled 0`, `tm_platform_reading_surfaces_abandoned 0`;
|
||||
- обе новые ручки конфигурации печатаются на буте с источником (`TM_PLATFORM_SWEEP_BUDGET` `2m0s`
|
||||
`default`, `TM_PLATFORM_RUN_BUDGET` `1m0s` `default`) — норма PD-114.
|
||||
|
||||
**Гейты доков:** `python3 docs/scripts/counts.py --check` — exit 0, литералы сходятся;
|
||||
`--lint` — exit 0.
|
||||
⚠ **Раскрываю правку ЧУЖОГО документа:** пак убил два якоря в `PLATFORM_P8_REVIEW_SESSION_PROMPT.md`
|
||||
(константы `sweepBudget`/`defaultRunBudget` переехали), и `--lint` был из-за этого КРАСНЫМ. Якоря
|
||||
пере-нацелены на новые места, абзац оставлен как описание НАЙДЕННОГО, а не текущего кода. Правил
|
||||
чужой промт сознательно и говорю об этом прямо: якоря умерли по моей вине, а красный гейт не
|
||||
обходят. Оркестратор вправе откатить эту правку — содержательного текста она не трогает.
|
||||
⚠ Первая моя проверка этого гейта дала EXIT=0 и была ЛОЖНОЙ: я мерил в момент, когда файла с
|
||||
якорями в дереве не было, поэтому линт его не сканировал. Критик полноты был прав, я нет.
|
||||
|
||||
### Спил пер-термной подписи банка (слово владельца 22.08) и что осталось контракту
|
||||
|
||||
**Заказ:** модель отменена `D39.144` ещё 16.08, реализации нет — спилить в зоне, оставить пометку
|
||||
будущим сессиям. Перед спилом — аудит воркфлоу: 5 линз (канон · код зоны · потребители ·
|
||||
ратификация · будущая пометка) + адверсариальная проверка каждого рискованного вывода, **31 агент**.
|
||||
|
||||
**Снято из зоны:** маршрут `POST /books/{bookId}/bank/decisions` · хендлер `bankDecisions` и
|
||||
wire-типы (`wireBankDecision`/`wireBankDecisions`/`wireBankReceipt`) · метод `SubmitBankDecisions`
|
||||
из интерфейса `Library` и из стора · типы `BankDecision`/`BankReceipt` · `ErrUnknownTerm` /
|
||||
`UnknownTermError` и их отображение в контрактный код · пины, которые их описывали.
|
||||
|
||||
**НЕ тронуто, и это проверено:** чтение банка (`GET /bank`, `BankTerm`, оси статуса и происхождения)
|
||||
· вокабула ДВИЖКА `proposed|in_progress|approved` в сайдкар-инжесте — она чужая и не отменялась ·
|
||||
сам стоп `awaiting_bank` и подпись как `resume` (D39.144 снимала ГЕЙТ, не СТОП) · таблица
|
||||
`bank_decisions` и её констрейнты: миграции релизные, а колонки `term_id`/`dst` — ровно то, что
|
||||
нужно будущей ПРАВКЕ термина. Данных под снос не было: на стенде ноль строк решений.
|
||||
|
||||
**Пометка на месте write-пути** (`internal/httpapi/reading.go`, «THE WRITE HALF OF THE BANK IS
|
||||
GONE»): что именно отменено, что ратификация ОСТАВИЛА пер-термным (правку и добавление термина),
|
||||
чего нет ни на одной поверхности, и что под это уже есть в схеме и в движке (`mined_delta`).
|
||||
|
||||
⚠ **Гейт размера поверхности сработал** — `len(contractSurface) < 15` поймал снятие маршрута. Пол
|
||||
сдвинут 15 → 14 **с причиной в комментарии**: пол, опущенный под ратифицированное сокращение, — не
|
||||
подгонка; опущенный без неё — подгонка. Два теста, стоявшие на снятой ручке как на «любом POST»,
|
||||
пере-нацелены на живой маршрут, а не удалены.
|
||||
|
||||
#### Насколько страдает контракт (главный вопрос владельца)
|
||||
|
||||
Канон **не лжёт про модель** — `D39.144` в него внесена и записана в четырёх местах верно
|
||||
(`openapi.yaml:443-448`, `:539-541`, `:707`, `:2498-2499`). Беда в другом: **рядом с новой моделью
|
||||
в полном рабочем обмундировании стоит старая** — путь, три схемы, четыре счётчика, — и обе зовутся
|
||||
словом «signing». Файл несёт ДВЕ модели подписи.
|
||||
|
||||
- **Прямая ложь — шесть мест** (четыре в каноне, два в компаньоне). Худшие: `:1830-1832`
|
||||
(«статус подтянется на следующей пересборке» — не подтянется, решения до движка не доезжают, что
|
||||
говорит сам же канон на `:1959-1963`) и компаньон `:672` («`POST /bank/decisions` НЕ ПОСТРОЕНО» —
|
||||
было построено в P7 и снято сегодня).
|
||||
- **Самопротиворечия — три.** Самое явное: `:443` «Signing the bank is ONE act over the whole of
|
||||
it» против `:477` «Signing is not a row edit» — заголовок описания ручки называет подписью САМУ
|
||||
РУЧКУ. Тридцать четыре строки между ними, и оба текста компилируются в сгенерированный клиент.
|
||||
Плюс `:1918-1922` («строка может быть решена и НЕ подписана, потому что отказ — тоже решение») —
|
||||
дословно отменённая модель — соседствует с `:1929-1932` («INFORMATIONAL, never a gate»).
|
||||
- **Дыра, которую спил вскрывает и которую надо назвать вслух:** `approve` + непустой `dst` был
|
||||
ЕДИНСТВЕННЫМ живым носителем того, что D39.144 оставила пер-термным — «поправить термин». А
|
||||
«добавить свой термин» модель называет, и в контракте его **нет вообще**: на 17 путей канона
|
||||
запись в банк была ровно одна. То есть после спила у собственной фразы канона `:539-541`
|
||||
(«correct what they care to, then release it with ONE resume») **нет ручки**.
|
||||
- **Счётчики вырождаются.** `pending_decisions` считается как «строки `proposed`, на которые НЕТ
|
||||
записи в `bank_decisions`»; писателя больше нет ⇒ это «сколько строк предложено», а `complete`
|
||||
истинно ровно тогда, когда read-out движка не несёт ни одной `proposed` (не константа: сайдкар
|
||||
несёт и `in_progress`, и `approved`). Поля обязательные на проводе (`EventBank.required`), поэтому оставлены как есть:
|
||||
менять их смысл — правка канона, а не зоны.
|
||||
- **Объём и вес:** ~17 мест в `openapi.yaml` (~150 строк из 2592) + ~8 в компаньоне. Вес —
|
||||
**ломающий минор 0.4.0 → 0.5.0**; мажора нет ни по политике («0.x ломает миноры весь бета-период»),
|
||||
ни по факту. Цена фронту нулевая ДОПОЛНИТЕЛЬНО: его зеркало ратифицированно стоит на 0.2.3.
|
||||
|
||||
**Пока канон не поправлен, деплой обслуживает на одну ручку меньше, чем объявляет.** Это записано
|
||||
строкой `PD-370` (зонная половина закрыта, контрактная открыта), и это осознанная цена спила, а не
|
||||
недосмотр.
|
||||
|
||||
### Волна 2: воркфлоу-ревью и что оно нашло в САМОМ паке
|
||||
|
||||
Заказ владельца: после возврата ревьюеров волны 1 и починки их находок — воркфлоу пошире, 10–30
|
||||
агентов, один Fable 5 на тяжелейший участок. Исполнено: **11 линз поиска + адверсариальный рефутер
|
||||
на КАЖДУЮ находку + синтез, 47 агентов, 0 ошибок**. Fable 5 — один, на свипе и деньгах.
|
||||
|
||||
**36 находок, 21 пережила рефутера, 15 опровергнуто.** После склейки дублей (голодовку расчётной
|
||||
фазы назвали пять линз, срез по байту — две) это **17 строк регистра**: `PD-353…PD-366` закрыты,
|
||||
`PD-367`/`PD-368`/`PD-369` открыты — они не мои решения и переданы как вопросы.
|
||||
|
||||
⚠ **Итоговая строка самого воркфлоу («ни одна находка не пережила проверку») — БАГ МОЕГО скрипта
|
||||
синтеза, а не вердикт.** Она противоречила сама себе (пустыми были и «выжившие», и «убитые»), и
|
||||
таблица пере-собрана из `journal.jsonl` — по одной записи на агента. Говорю прямо: доверься я
|
||||
сводке, пак уехал бы на приёмку с блокером внутри.
|
||||
|
||||
**Блокер, найденный ПЯТЬЮ независимыми линзами (включая Fable 5) и подтверждённый мной по коду
|
||||
до их вердикта — `PD-353`.** Пак разделил проход на фазы, чтобы одна не могла съесть другую, и
|
||||
оставил ВНУТРИ второй фазы то же самое голодание: `UnsettledRuns` упорядочен старейшим-первым,
|
||||
каждый элемент стоит целый бюджет, а `settle` при нечитаемой трате возвращает nil — то есть клин
|
||||
рапортует УСПЕХ, потратив проход. Одна недоступная книга замораживала холды ВСЕХ аккаунтов, каждый
|
||||
проход. Это ровно та формулировка, которую шапка `Sweep` объявляла побеждённой.
|
||||
|
||||
**Ещё два, которые делали механизмы пака недостижимыми:**
|
||||
- `PD-354` — счётчик неудач обнулялся ТИШИНОЙ: дорогой вопрос движку задаётся раз в `ResyncEvery`,
|
||||
остальные проходы ничего не устанавливают, и `ClearRunDeferral` на них сбрасывал счёт. 1,0,1,0 —
|
||||
`StalledAfter` недостижим, гейдж пуст, оператору не говорят никогда.
|
||||
- `PD-355` — колонка SPENT в операторской таблице показывала пожизненную трату КНИГИ. Моя же
|
||||
починка G8 волны 1 взяла не то число, на единственной таблице, заведённой ради решения о деньгах.
|
||||
|
||||
**Чему меня научили РЕФУТЕРЫ (15 опровержений, каждое куплено исполнением, а не рассуждением):**
|
||||
- «Поднять `RunBudget` = объявить здоровый прогон застрявшим» — **опровергнуто замером**: поднятие
|
||||
ручки не меняет вообще ничего, а строгая проверка `RunBudget < SweepBudget/2` отвергла бы сами
|
||||
шиппящиеся дефолты. Я уже держал эту «починку» в плане и не построил её. Остаток — `PD-368`.
|
||||
- «Интейк обязан применить `Whole()`» — **опровергнуто контрактом и батареей**: вся батарея интейка
|
||||
ездит на манифестах без списка глав, контракт интейка — только счётчики. Механика находки верна
|
||||
(`PD-367` открыт), лекарство — нет.
|
||||
- «Пропуск тела помощника в гейте SQL = дыра» — **опровергнуто исполнением**: мутация в теле `bump`
|
||||
краснит десятки поведенческих тестов; спрятать там непроверяемый оператор нельзя.
|
||||
- «Стойл-детектор слеп к форме PD-162» (находка Fable 5) — опровергнуто.
|
||||
|
||||
**Две линзы разошлись во ВЕСЕ одной находки** (`PD-358`, срез по байту в операторской таблице):
|
||||
одна назвала микрополировкой вне рамки, вторая довела цепочку до многобайтового stderr движка.
|
||||
Починил как дешёвое и бесспорное, расхождение раскрыл в строке.
|
||||
|
||||
### Самопроверка (артефакт §5: имена агентов и по одной команде или цитате из каждого)
|
||||
|
||||
Исходов три, «не проверено» ≠ «опровергнуто». Каждому агенту выдан СВОЙ каталог песочницы явным
|
||||
путём, мандат читающий, state-changing git запрещён перечислением (D39.113). В рабочее дерево не
|
||||
писал никто: оба ревьюера волны 1 ответили на прямой вопрос уликами.
|
||||
|
||||
**Волна 1 — три изолированных ревьюера по финальному диффу (author≠reviewer):**
|
||||
|
||||
| Агент | Ось | Цитата или команда ИЗ ЕГО отчёта | Находок | Исход |
|
||||
|---|---|---|---|---|
|
||||
| `agent-afec473415b228cdf` | контракт-конформность против канона 0.4.0 + свои посадки | «в песочнице создан симлинк `<scratch>/docs -> …/docs` — новый гейт `internal/gates/contract_test.go` тянется за канон ЗА пределы модуля, и без него копия дерева его не находит» | 5 + 3 пережившие мутации | все 8 починены |
|
||||
| `agent-a394edded388676bc` | шов с движком и деньги, свой инвентарь каналов | «`repo*/platform/cmd/rev2probe\|rev2ctl\|rev2seam\|rev2ceiling\|rev2ids/main.go` и `repo*/platform/internal/runs/rev2_starve_test.go` — мои пробы (только в копиях)» | 8 + 3 пережившие мутации | все 11 починены; G1 — БЛОКЕР в моём коде |
|
||||
| `agent-a1e4407d89e52128d` | полнота сдачи: пункт заказа → носитель в дереве | «Snapshot taken at 2026-08-22 00:49:24. ⚠ The tree was moving while I read it: `platform/BACKLOG.md` gained rows П-19/П-20 at 00:48, mid-review» | 6 | 5 починены, 1 (К-4) вскрыла МОЮ ошибку измерения |
|
||||
|
||||
Две разведки (read-only, не ревью): `agent-acf370d564663617e` — инвентарь SQL для границы sqlc;
|
||||
`agent-a1cb3d1a5c59f2f2e` — инвентарь каналов движка.
|
||||
|
||||
**Волна 2 — воркфлоу `wf_f8d1f3e6-85a`, 11 линз (одна на Fable 5) + рефутер на каждую находку:**
|
||||
|
||||
| Линза (агент) | Предмет | Цитата или команда ИЗ ЕГО отчёта | Находок | Пережило |
|
||||
|---|---|---|---|---|
|
||||
| `a2fdcd13144058b57` **Fable 5** | свип и деньги — тяжелейший участок | «Стойл-детектор слеп к клину „юнит исчез, workdir недоступен“ (форма PD-162): реконсиляция каждый проход рапортует успех» | 3 | 2 |
|
||||
| `ae8f856fe08ee9e61` | фазы и отсрочка свипа | «Счётчик отсрочек не может дорасти до StalledAfter для того самого клина, ради которого написан» | 4 | 3 |
|
||||
| `a3c4dc82a7d62f8a6` | архитектурный стиль, велосипеды | «Фаза расчёта денег осталась без отсрочки: head-of-line starvation, от которой пак и построен, на списке `UnsettledRuns` не починена» | 3 | 1 |
|
||||
| `a62786a00eb5bbae4` | каноны Go и каноны зоны | «RunBudget молча обрезан половиной SweepBudget» (опровергнуто) + голодовка расчётной фазы | 4 | 2 |
|
||||
| `af8620a80b7e12b06` | материализация и пол манифеста | «Провалившийся экспорт на смене кадра затирает весь оплаченный текст книги» | 3 | 3 |
|
||||
| `ada2420ac7c576856` | CLI, метрики, конфиг | «SPENT в операторской таблице — пожизненный счётчик КНИГИ, а не траты этой попытки» | 3 | 3 |
|
||||
| `ac92e0a8acba148ec` | сам гейт SQL: даёт ли ложную зелень | «`unresolvable` ключёвана функцией, а не сайтом: второй несворачиваемый запрос в той же функции молча проверяется текстом первого» | 2 | 1 |
|
||||
| `a21f43459f3f12f7e` | комментарии: вода против несущего | «perf_test.go копирует „нормативные“ цифры из миграции 00022 и в той же фразе утверждает, что не копирует» | 4 | 1 |
|
||||
| `a8faf63f1dde50903` | скоуп: сверка построенного против БУКВЫ промта | «Пункт 9: новое условие замера в perf_test.go ложно для бенчмарка этого же файла» | 4 | 2 |
|
||||
| `a4dad647c0eb8310f` | регрессии: что пак сломал в уже работавшем | «Отмена контекста сообщается как дедлайн: остановка демона посреди прохода поднимает счётчик голодания» | 3 | 2 |
|
||||
| `a178aca863af19915` | контракт-конформность построенного против канона 0.4.0 | «Пункт 1: подъём константы оставил в коде утверждение, что деплой объявляет 0.3.0» (опровергнуто) | 3 | 1 |
|
||||
|
||||
Рефутеры (по одному на находку, 36 штук) считаются наравне с находками — три примера того, чем
|
||||
куплены их вердикты:
|
||||
- `ad12d9bcca05b883a`: «`WITH the index n_tup_upd=3942 n_tup_hot_upd=3909 (HOT 99.2%) idx size=8192`
|
||||
против `WITHOUT the index n_tup_upd=1994 n_tup_hot_upd=1984 (HOT 99.5%)`» — из этого замера
|
||||
выросла `PD-364` (индекс удалён);
|
||||
- `a337318688be20d30`: «`finished_at = now()` → `books.go:313: column "finished_at" of relation
|
||||
"books" does not exist (42703)` … на всех 5 сайтах»;
|
||||
- `a7fadd3c541e9a87d`: «пять проходов подряд дают „the sweep settled 2 of 3 finished runs: context
|
||||
deadline exceeded“, третий, чистый прогон не достигается никогда, reserved держится на $0.90» —
|
||||
это подтверждение блокера `PD-353` исполнением.
|
||||
|
||||
**Посадки мутаций волны 2 — 6, поймано 6:** фаза расчёта не отсрочивает ✅ · `UnsettledRuns`
|
||||
игнорирует отсрочку ✅ · тишина снова считается здоровьем ✅ · `RunBudget` снова не присваивается ✅ ·
|
||||
срез по байту в операторской таблице ✅ · управляющие символы не вычищаются ✅.
|
||||
⚠ Две первые попытки посадить последние две дали НЕ падение теста, а ошибку сборки (осиротевший
|
||||
импорт) — это слабее, и они пере-посажены компилирующимися.
|
||||
|
||||
### Релей §4.7: не приходил
|
||||
|
||||
Аддендум пака P8-REVIEW должен был доехать **только релеем оркестратора с эхо-подтверждением**.
|
||||
За сессию релея не поступало: ни находок по четырём осям, ни эхо. Отчёт читающего пака я не читал
|
||||
и не гадал по нему — промт это прямо запрещает, состав пака определяет оркестратор.
|
||||
|
||||
Единственное касание того пака — техническое и раскрыто выше: два его якоря умерли от МОИХ правок
|
||||
кода, `--lint` покраснел, якоря пере-нацелены (`docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md:62`,
|
||||
плюс третье пере-нацеливание 22.08 после спила). Содержательного текста чужого промта не трогал.
|
||||
|
||||
### Obstacle: что не удалось и что не проверено
|
||||
|
||||
- **Сквозного живого перевода через платформу не было** и в этом паке быть не могло: строка 202
|
||||
гейчена холодным прогоном движка (решение владельца 20.08). Всё после «прогон стартовал и
|
||||
остановился» доказано батареей против живого Postgres и настоящего `tmctl`, а не переводом.
|
||||
- **Труба 199(а) не построена и форматы сверены ЧТЕНИЕМ**, а не записью файла: `mined_delta` и
|
||||
`mined_rejects` прочитаны в коде движка (`membank.LoadGlossarySeed`, `mining.go`), но ни одного
|
||||
файла платформа не писала — писать было бы строительством нератифицированной развилки.
|
||||
- **Замер `PD-297` не снят.** Отказ осознанный: путь самый опасный на запись, править его надо с
|
||||
замером на корпусной книге до и после, а корпусной книги в паке не было.
|
||||
- **Финальный дифф ПОСМОТРЕН** независимым ревьюером (author≠reviewer) уже после спила — он и
|
||||
закрыл эту строку obstacle. Три его major были настоящими дырами в МОЁМ коде, и все три —
|
||||
отсутствие пина, а не ошибка логики: пол `ErrTextUnknownForANewCut` (охраняет единственную
|
||||
запись, способную стереть оплаченный текст) · «стоп перевешивает отсрочку» в `RunsToReconcile` ·
|
||||
«пересборка банка не трогает `bank_decisions`» (свойство, на которое опирается пометка, ради
|
||||
которой спил и делался). Каждая закрыта пином, каждый пин проверен посадкой мутации. Плюс семь
|
||||
минорных: комментарии `SaveBank` и `bankCountsTx` описывали отменённую модель как живую,
|
||||
пометка врала про `action NOT NULL`, снятие NUL не было запинено. Не найдено дефектов там, где
|
||||
он искал отдельно: пол поверхности 15→14 равен реальному размеру таблицы, пере-нацеленные тесты
|
||||
проверяют то же свойство, лишнего не срезано.
|
||||
- **Число скипов батареи** снято рецептом `make check`, который пишет `.check.log` в дерево; при
|
||||
параллельных прогонах агентов лог обрезался. Отдельная сверка двух пакетов с условными скипами
|
||||
(`internal/runner`, `internal/gates`) дала `--- SKIP` = 0.
|
||||
- **Контрактная половина `PD-370` открыта**: пока канон не поправлен, деплой обслуживает на одну
|
||||
ручку меньше, чем объявляет. Это цена спила, названная заранее, а не обнаруженная при приёмке.
|
||||
|
||||
### Инвентарь каналов движка (артефакт §4.4 — собран ЧТЕНИЕМ кода движка, не по нашим докам)
|
||||
|
||||
Пак коснулся канала движка (пункт 10: `ingest.Manifest.Whole()` построен как зеркало движкового
|
||||
правила), поэтому инвентарь сдаётся таблицей, а не словами «я прочитал код движка».
|
||||
|
||||
| Канал | Писатель в движке | Атомарность | Читатель/писатель платформы | Согласовано? |
|
||||
|---|---|---|---|---|
|
||||
| `<project_db>.manifest.json` | `pipeline/manifest.go:379` `writeFileAtomic` | **атомарно** (temp+`Sync`+rename, `pipeline/artifact.go:24-59`) | `internal/runner/engine.go` (`tmctl manifest`) → `ingest.DecodeManifest` → `internal/books/parse.go`, `internal/readmodel` | ✅ **и стало строже**: `Manifest.Whole()` теперь зеркалит `BookManifest.selfConsistent` (`pipeline/manifest.go:460-483`) поле в поле по тем правилам, которые платформа вообще читает (главы, юниты, нумерация). Верхняя граница чанков движка у нас counterpart'а не имеет намеренно — платформа счётчиков чанков не берёт |
|
||||
| `<project_db>.bank.json` | `pipeline/bankexport.go:154` `writeFileAtomic` | **атомарно** | `internal/runner/artifacts.go:37` → `ingest.DecodeBank` → `pgstore.SaveBank` | ✅ |
|
||||
| `<project_db>.bank-stop.json` | `pipeline/mining.go:393` `writeFileAtomic` | **атомарно** | **читателя НЕТ** (грепом по зоне — ноль вхождений) | ⚠ канал существует и не потребляется; полная таблица подписи платформе сегодня не нужна, она проецирует банк |
|
||||
| `<project_db>.bank-stop.txt` | `pipeline/mining.go:195` `os.WriteFile` | **НЕ атомарно** (усечение первым делом) | читателя нет | ✅ и это важно: брать его на живом прогоне нельзя — исправление пинга №15 оркестратором подтверждается кодом |
|
||||
| `<project_db>.mined-signature.yaml` | `pipeline/mining.go:188` `os.WriteFile` | **НЕ атомарно** | читателя нет | ✅ то же |
|
||||
| `<project_db>.auto-bank.yaml` | `pipeline/mining.go:741` `os.WriteFile` | **НЕ атомарно** | читателя нет | ✅ то же |
|
||||
| `mined_delta` (путь из `book.yaml`) | **писателя в движке НЕТ** — только читатель `pipeline/mining.go` `loadMinedDelta`; формат `seed.File` (`internal/seed/seed.go:63-80`), грузится `membank.LoadGlossarySeed`, `Source` пере-штампуется на `"mined"` | — | **писателя НЕТ и у платформы** | ❌ **разрыв — это и есть строка 199(а)** |
|
||||
| `mined_rejects` | читатель `pipeline/mining.go` `loadMinedRejects`; формат `rejects: [{src, note}]`; ФИЛЬТР ПРЕДЛОЖЕНИЙ, в банк не входит | — | писателя нет | ❌ тот же разрыв |
|
||||
| `events.jsonl` (NDJSON эмиттера) | движок, StreamVersion 1.1 | append-only | `internal/ingest/tail.go` + `pgstore.RunSink` | ✅ пак не трогал |
|
||||
| exit-коды `tmctl` | контракт движка | — | `ingest.OutcomeOf`, `internal/runs/reconcile.go` `outcome` | ✅ пак не трогал |
|
||||
| `tmctl status --json` | движок | — | `internal/runner/engine.go` → `ingest.StatusReport`; расчёт денег | ✅ пак не менял форму; изменил лишь то, СКОЛЬКО раз его зовут при неудаче (бэкофф) |
|
||||
| `book.yaml` | оператор (шаблон) + **платформа один раз** (`internal/books/render.go`, `O_EXCL`) | — | платформа ПИШЕТ пять фиксированных ключей | ⚠ **декодер движка СТРОГИЙ** (`config/book.go:142-144`, `KnownFields(true)`): незнакомый ключ = жёсткая ошибка. Значит любое расширение набора ключей платформой — ратификация, а не правка. Прямо относится к развилке 199(а) выше |
|
||||
|
||||
|
|
@ -6,694 +6,36 @@
|
|||
|
||||
## Текущее состояние
|
||||
|
||||
- **P8-FIX ПРИНЯТ И ЗАЛЕНДЕН оркестратором №18 (22.08).** Приёмка велась слепой линзой ДО чтения
|
||||
отчёта: сперва пере-прогон, счёт тестов и собственные посадки, и только потом сверка с запиской
|
||||
зоны. **Что сверено ИСПОЛНЕНИЕМ, а не принято на слово:**
|
||||
- **Батарея на моём стенде:** 18 пакетов, exit 0, линтер «0 issues» — сходится. ⚠ **«Скипов 0» НЕ
|
||||
воспроизведён: у меня три.** Это не регрессия пака и не ошибка отчёта — `systemdOrSkip` гейтит
|
||||
три теста `internal/runner` достижимостью пользовательского менеджера systemd, а на моём хосте
|
||||
`/run/user/1000` не существует. Отчёт мерил на хосте с живым менеджером и там 0. Незакрытым
|
||||
остаётся то, что рецепт объявляет у батареи ДВА гейта, а их три — `PD-374`.
|
||||
- **Тесты 561 против 532 на HEAD** — сходится, сверено `git show HEAD:<файл>` по всем `_test.go`
|
||||
против дерева. Удалённых имён ровно **три**, все три описывали снятую пер-термную подпись.
|
||||
- **Отпечатки миграций:** пере-считаны все 25, расхождений нет (⚠ испр. 22.08 аудитом доков: первая редакция этой строки писала «41» — число взято из головы, а не из команды; `grep -c '^[0-9a-f]\{64\}' migrations.sha256` = 25, файлов миграций тоже 25); в диффе `migrations.sha256` ровно
|
||||
одна ДОБАВЛЕННАЯ строка — заявление «релизные миграции не тронуты» держится механически.
|
||||
- **Пол поверхности 15 → 14** равен реальной длине таблицы маршрутов (14), то есть запаса не
|
||||
оставлено и следующее сужение гейт поймает. Причина стоит в комментарии рядом.
|
||||
- **Гейты доков:** `--check` exit 0 · `--lint` exit 0. **Пинг №1 зоны закрыт:** якорь
|
||||
`05-decisions-log.md:542` → `perf_test.go:13` жив, токен «96% of the page» на месте. Правку
|
||||
ЧУЖОГО промта (пере-нацеливание двух моих якорей после переезда констант) **утверждаю и не
|
||||
откатываю**: якоря умерли по вине пака, пак их починил и раскрыл это сам — это ровно то
|
||||
поведение, которого норма и требует.
|
||||
- **Деньги — двумя путями из СЫРОГО леджера, своей живой пробой** (свой дев-демон на дереве пака,
|
||||
своя БД): до `run abandon --release-hold` кэш `account_balances` = 24910000 и `sum(credit_ledger)`
|
||||
= 24910000 при одном открытом холде 90000; после — 25000000 и 25000000, открытых холдов 0, строки
|
||||
`grant · hold · hold_release`. Холд вернулся целиком, книга разморозилась (следующий прогон 202).
|
||||
- **Вся цепь PD-169 воспроизведена мной живьём, а не по отчёту:** отсрочка считает неудачи между
|
||||
проходами (`FAILS 6`), переходит `StalledAfter` → гейдж `tm_platform_runs_stalled 1` на проводе →
|
||||
`tmplatformctl runs --stalled` называет прогон, счёт, что держит и НАСТОЯЩУЮ ошибку движка. Обе
|
||||
новые ручки печатаются на буте с источником (`SWEEP_BUDGET 2m0s default`, `RUN_BUDGET 1m0s default`).
|
||||
- **Одиннадцать собственных посадок мутаций вне списка автора.** Убиты восемь: разделение фазы ·
|
||||
«бюджет выбран = отказ» · очистка счёта тишиной · бэкофф · пропуск фазы расчёта на исчерпанном
|
||||
проходе · версия контракта · пол идентичностей манифеста · «стоп перевешивает отсрочку». ⚠ Две
|
||||
первых редакции моих посадок дали ЛОЖНЫЕ «выжила» — я гонял не тот пакет (пин лежал в
|
||||
`internal/runs`, а мутация была в `internal/pgstore`); пере-прогнано, обе убиты. Говорю об этом,
|
||||
потому что необъявленная ошибка харнесса приёмки — это находка, которой нет.
|
||||
- **Выжили три, и они разные.** `StalledAfter` — законно: носитель один, пин ездит на константе.
|
||||
`maxAttempts` — **`PD-373`**: второй носитель есть в рантбуке прозой. `truncateReason` в
|
||||
`DeferRun` — **`PD-372`**: пин ездит на самой функции, ни один из четырёх вызовов не проверен.
|
||||
- **Находка вне карты пака — `PD-371`:** исключение «стоп перевешивает отсрочку» не имеет границы,
|
||||
и это видно живьём: `NEXT TRY` стоял на 30 минут вперёд, а счётчик рос каждые 15 секунд.
|
||||
**Регистр после приёмки: 374 строки, открытых 72 (1 major, 16 minor, 55 info)** — своих строк
|
||||
четыре (`PD-371`…`PD-374`); зонные числа сдачи (370/68) остаются верны на момент сдачи и не
|
||||
переписываются. **Открытым уезжает `PD-370`** (контрактная половина) — это НЕ работа зоны, и
|
||||
закрывать её здесь было бы подгонкой под критерий приёмки; согласен с диспозицией.
|
||||
⚠ **Порядок паков вышел ОБРАТНЫМ моей очереди, и это меняет рамку следующего:** промт P8-FIX
|
||||
строкой 3 требует запуска «ПОСЛЕ пака P8-REVIEW», запущен он был раньше. Следствие в плюс: цена,
|
||||
которую я назвал вслух при перестановке — «блокер свипа живёт всё время читающего пака», — НЕ
|
||||
заплачена, блокер закрыт первым. Следствие в минус: §4.7 (аддендум релеем) в этом паке пустой и
|
||||
честно назван таковым, а читающий пак теперь пойдёт по коду, который этот пак только что переписал —
|
||||
его промт по оси 3 зона уже пере-нацелила на новые места, остальные три оси не затронуты.
|
||||
|
||||
- **P8-FIX — ФИКС-ЛИСТ ПРИЁМКИ P7 ЗАКРЫТ. Дерево готово к приёмке и лендингу (22.08).**
|
||||
Батарея: 18 пакетов, exit 0, **скипов 0**, линтер 0 issues, скипов 0, тестов **561** (HEAD 532). Регистр —
|
||||
**370** строк, открытых **68** (14 minor, 53 info, **1 major**) — сверено командой на момент сдачи
|
||||
(`python3 docs/scripts/counts.py --check`, exit 0; `--lint` exit 0; ⚠ вес регистра этим скриптом НЕ
|
||||
проверяется — числа сняты его печатью, а не гейтом). **Открытый major — `PD-370`**, контрактная
|
||||
половина спила пер-термной подписи банка: она не работа зоны, закрывать её здесь было бы подгонкой
|
||||
под критерий приёмки. Миграция пака: **00025**. Дерево не закоммичено и не застейджено.
|
||||
**Удалённых имён тестов ТРИ, и все три — по решению владельца 22.08** (сверено исполнением, не
|
||||
памятью: имена из `git show HEAD:<файл>` по всем `_test.go` против дерева):
|
||||
`TestABadDecisionRefusesTheWholeBatchAndNamesTheItem` · `TestAnUnknownTermRefusesTheBatch` ·
|
||||
`TestTheBankIsReplacedWhileTheDecisionsSurvive` — они описывали пер-термную подпись банка, которую
|
||||
`D39.144` отменил, а этот пак снял из кода (`PD-370`). Тест снятого кода — не потеря покрытия;
|
||||
третий переписан на СОХРАНИВШЕЕСЯ свойство (`TestTheBankIsReplacedFromTheEnginesReadOut`).
|
||||
Плюс один ПЕРЕИМЕНОВАН по факту изменения смысла (`…GivesTheHoldBackOnlyWhenAsked` →
|
||||
`…AlwaysGivesTheHoldBack`, `PD-361`) — имя заведено этим же паком.
|
||||
⚠ **Пинг оркестратору №1 — ЕГО зона, сверить при лендинге:** `docs/architecture/05-decisions-log.md:542`
|
||||
держит якорь на `internal/pgstore/perf_test.go:13` с токеном «96%». Пак ЭТОТ комментарий правил
|
||||
(`PD-366`: он лгал про свой же бенчмарк), токен я сохранил намеренно и `--lint` зелёный — но док
|
||||
чужой, поэтому говорю, а не считаю вопрос закрытым.
|
||||
⚠ **Пинг оркестратору №2 — попутная находка ВНЕ зоны пака:** `PD-369` / `П-21` — `ClaimIdempotency`
|
||||
под конкуренцией отвечает 500 (1–2 падения на ~80 прогонов собственного теста). Файл в диффе пака
|
||||
не участвует, не трогал.
|
||||
⚠ **Пинг оркестратору №3 (`PD-370`, требует контрактной сессии):** канон 0.4.0 несёт пер-термный
|
||||
`BankDecision{action: approve|decline}`, а `D39.144` эту модель отменил 16.08 — подписывается весь
|
||||
банк одним ОК. Зона канон исполняет, снять в одностороннем порядке не может. Правка канона →
|
||||
правка зоны, в этом порядке.
|
||||
⚠ **Сдача идёт ПОСЛЕ второй волны ревью**, которая нашла в самом паке блокер (`PD-353`) и два
|
||||
дефекта, делавших его собственные механизмы недостижимыми (`PD-354`, `PD-355`) — раздел «Волна 2».
|
||||
|
||||
### Записка-план по §4 промта: пункт → где исполнено / отказ с причиной
|
||||
|
||||
| Пункт | Исход | Где |
|
||||
|---|---|---|
|
||||
| **Блокер свипа** (врезан первым) | **исполнен четырьмя механизмами** | ниже, отдельным разделом; `PD-169` пере-открыт и закрыт заново |
|
||||
| 1. `ContractVersion` 0.3.0 при формах 0.4.0 | исполнен | `internal/httpapi/capabilities.go` + гейт `internal/gates/contract_test.go` (`PD-328`) |
|
||||
| 2. Пины на два поля `EventBase` | исполнен, посадка сперва воспроизведена | `internal/pgstore/events_test.go` (`PD-329`) |
|
||||
| 3. Бюджет попыток материализации | исполнен | `internal/readmodel/readmodel.go`, миграция 00025 (`PD-330`) |
|
||||
| 4. sqlc + гейт разбора SQL | **гейт построен; sqlc — граница названа, решение владельца** | `internal/pgstore/sqlgate_test.go`; разбор — `PD-44`, строка бэклога **П-19** |
|
||||
| 5. `PD-297` (round-trip под блокировкой) | **отказ с причиной** | нечем и не на чем мерить — ниже |
|
||||
| 6. Труба доставки решений банка (строка 199а) | **не строил, сдаю вопросом** — как промт и требует | ниже, отдельным разделом |
|
||||
| 7. Мусор: задвоенная строка доккомментария | исполнен | `internal/books/books.go` `canTranslate` (`PD-335`) |
|
||||
| 8. Ревью-пак четырёх осей | **НЕ мой** — заказ пака P8-REVIEW | — |
|
||||
| 9. Один носитель на факт для замера 00016→00022 | исполнен, с названным расхождением с буквой пинга | `internal/pgstore/perf_test.go` (`PD-336`) |
|
||||
| 10. Пол на пустой манифест у материализатора | исполнен | `internal/ingest/manifest.go` `Whole()`, `internal/readmodel/readmodel.go` (`PD-332`) |
|
||||
| 11. `PD-327` стоит open при ратифицированном каноне | исполнен | регистр, `fixed(P8-FIX)` |
|
||||
| 12. Изоляция читающих чтений не запинена | исполнен, посадка сперва воспроизведена | `internal/pgstore/isolation_test.go` (`PD-333`) |
|
||||
| 13. `CreditHeldBy`: оговорка не запинена | исполнен, посадка сперва воспроизведена | `internal/pgstore/credits_test.go` (`PD-334`) |
|
||||
| `PD-255` (норма комментариев сменилась) | закрыт решением владельца 21.08 | регистр |
|
||||
|
||||
### Блокер свипа: почему лечение — не «поднять константу»
|
||||
|
||||
Диагноз пинга подтверждён построчно и **воспроизведён исполнением**. Живое свойство: проход 2 мин
|
||||
против 60 с на прогон ⇒ двух медленных прогонов хватает, чтобы съесть проход целиком, после чего
|
||||
`ctx.Err()` обрывает цикл и `UnsettledRuns` — единственный ретрай отложенного расчёта — не
|
||||
вызывается ВООБЩЕ, для всей инсталляции, каждый проход. Построено:
|
||||
|
||||
1. **Проход разделён на фазы.** Реконсиляция получает половину остатка, расчёт — свой кусок
|
||||
(`runs.phaseBudget`). Выбранный бюджет фазы больше не ошибка свипа: очередь просто ждёт тика.
|
||||
2. **Отсрочка с растущим бэкоффом на ПОПЫТКЕ** (`run_attempts.reconcile_after`, миграция 00025).
|
||||
Свип читает `RunsToReconcile`, а не `ListLiveRuns`; порядок «старейший первым» не тронут — он
|
||||
верен для всего, что просто медленно, — а из ГОЛОВЫ заклиненный прогон убирает именно отсрочка.
|
||||
Телеметрия (`Lag`) по-прежнему видит всё.
|
||||
3. **Терминальное состояние после N неудач** (`runs.StalledAfter` = 5, ≈ четверть часа попыток):
|
||||
прогон считается ЗАСТРЯВШИМ, гейдж `tm_platform_runs_stalled`, одна ERROR-строка на пересечении
|
||||
порога — не на каждом тике.
|
||||
4. **Ручка у ОПЕРАТОРА, которой не было вовсе:** `tmplatformctl runs [--stalled]` называет строку,
|
||||
число неудач, следующую попытку, сумму холда и его возраст, юнит и последнюю ошибку;
|
||||
`tmplatformctl run abandon --run <id> --reason <text> [--release-hold]` — терминальный вердикт.
|
||||
Плюс `books --abandoned` и `book refresh` для второй половины (материализация).
|
||||
|
||||
**Границу «дальше не иду» называю явно:** автоматически решать про деньги прогона, о котором нельзя
|
||||
спросить, этот пак НЕ стал. Правило реконсилятора везде — «я не смог спросить» ≠ «прогона нет»;
|
||||
прогон, упавший N раз, это ровно прогон, о котором никто не смог спросить, и счётчик перед той же
|
||||
ошибкой её не чинит. Автоматический ответ на этот вопрос и есть эскроу (строка 136, П-18), который
|
||||
промт §4.6 из пака исключает. Что счётчик покупает — что человека ИЗВЕЩАЮТ; что покупает ручка —
|
||||
что извещение чего-то стоит. Хвост записан строкой **П-20**, а не подразумевается.
|
||||
|
||||
Оба числа стали ручками: `TM_PLATFORM_SWEEP_BUDGET` и `TM_PLATFORM_RUN_BUDGET`. Второе —
|
||||
отдельная находка пака: поле `runs.Config.RunBudget` было объявлено, задокументировано и **никем не
|
||||
присваивалось** (`PD-331`), то есть ручка существовала в структуре и не делала ничего.
|
||||
|
||||
### Пункт 4: что построено и где проходит граница sqlc
|
||||
|
||||
**Построен гейт, который доходит туда, куда sqlc не дойдёт:** `TestEverySQLStatementParsesAgainstTheMigratedSchema`
|
||||
сворачивает КАЖДЫЙ SQL пакета прямо из исходника (литералы, конкатенации, именованные константы,
|
||||
плюс оба внутренних помощника, принимающих SQL параметром) и планирует его живым Postgres'ом
|
||||
(`explain (generic_plan)`) против мигрированной схемы. **162 оператора, все планируются.**
|
||||
Несворачиваемый SQL — ОШИБКА гейта, а не пропуск: единственное исключение выписано таблицей в самом
|
||||
гейте (`store.go` `Ready`, где имя таблицы принадлежит goose). Тем же гейтом закрыт вопрос
|
||||
фикс-листа «есть ли в read-модели запрос, которого не касается ни один тест»: теперь его касаются
|
||||
все, на каждом прогоне батареи.
|
||||
|
||||
**Граница sqlc — мой выбор, аргумент:** склеек в пакете 25 из 147, фрагментов-констант 15, у
|
||||
`lastRun` девять потребителей — read-модель для sqlc недостижима по построению, и это ровно та
|
||||
часть, где рантайм-ошибки и случались. Свободных от склейки файлов целиком пять: `credits.go`(15) ·
|
||||
`identity.go`(13) · `idempotency.go`(7) · `sessions.go`(5) · `observe.go`(1) = **41 запрос**.
|
||||
Это единственный кусок, где инструмент силён; конверсия — отдельный пак (одиннадцать денежных
|
||||
запросов идут внутри чужой транзакции, генерённый код коммитится, нужен пин версии и гейт
|
||||
«сгенерённое актуально»). Взять его здесь на две-три ручки — ровно то, от чего предостерёг
|
||||
фикс-лист: купить инструмент туда, где не болит. Заведено строкой **П-19**.
|
||||
⚠ **Вопрос закрыт владельцем 22.08: sqlc берётся ОТДЕЛЬНОЙ СЕССИЕЙ, не внутри пака** — изменений
|
||||
много и мешать их с содержательной работой нельзя. Граница остаётся названной: 41 запрос.
|
||||
|
||||
### Пункт 5, `PD-297`: отказ с причиной
|
||||
|
||||
Мерить нечем и не на чем. Сама строка регистра объявляет замер на здешнем стенде НЕпредставительным
|
||||
(unix-сокет против управляемого PG по TCP — разница на два порядка), а корпусной книги нет: она
|
||||
появляется на холодном прогоне движка, которым гейчена строка 202 (решение владельца 20.08). Правка
|
||||
самого опасного на запись пути без замера — ровно то, что эта строка запрещает. Берётся вместе с
|
||||
холодным прогоном.
|
||||
|
||||
### Пункт 9: расхождение с буквой пинга, названное вслух
|
||||
|
||||
Пинг просил свести условия В миграцию 00022. **Она РЕЛИЗНАЯ**, и гейт неизменности
|
||||
(`migrations.sha256` + `TestReleasedMigrationsAreUnchanged`) её правку запрещает; правка гейта ради
|
||||
этого была бы подгонкой под зелень, что промт и `CLAUDE.md` запрещают прямо. Поэтому 00022 остаётся
|
||||
нормативным носителем вакуумированных цифр как есть, а условия дописаны в носители, которые править
|
||||
законно. «96%» не удалено и получило своё условие: это ХОЛОДНЫЙ корпус акта 4 (636 мс против 24),
|
||||
тогда как 80–83% — вакуумированный. Носители мерили разное и все честны.
|
||||
|
||||
### Строка 199(а): развилку НЕ решал — сдаю вопросом, как требует §4.2 промта
|
||||
|
||||
⚠ **ПОПРАВКА ВЛАДЕЛЬЦА 22.08, и она сносит посылку, на которой я строил разбор.** Я описывал трубу
|
||||
как доставку ПЕР-ТЕРМНЫХ решений («approve/decline на термин»). **Такой модели нет и не было
|
||||
с 16.08:** D39.144 ратифицировал ОДИН ОК над ВСЕМ банком — «пер-термная подпись = сотни кликов —
|
||||
НЕ модель продукта», «в модели одного ОК сценария „какие 17 из 300 решены“ не существует».
|
||||
Пер-термно существует не подпись, а **правка**: поправить/добавить термин до подписи, и — после
|
||||
прочтения — поправить термин с пере-генерацией задетых глав (вторая половина гейчена строкой 192).
|
||||
|
||||
Труба от этого не отменяется, но её ГРУЗ другой: не «решения», а **правки терминов** (`src` → новый
|
||||
`dst`, плюс добавленные) в `mined_delta`. Развилка ниже (кто объявляет ключи и кто создаёт файлы)
|
||||
от этого не меняется ни в одном пункте — она про владение `book.yaml`, а не про груз.
|
||||
|
||||
⚠ **Модель, которую D39.144 отменил, была у зоны ПОСТРОЕНА — 22.08 СНЯТА по слову владельца**
|
||||
(`PD-370`). Что ушло: маршрут `POST /books/{bookId}/bank/decisions`, хендлер и wire-типы, метод
|
||||
интерфейса `Library`, `SubmitBankDecisions`, типы `BankDecision`/`BankReceipt`, `UnknownTermError`
|
||||
и их пины. На месте write-пути — пометка для будущей сессии (`internal/httpapi/reading.go`, блок
|
||||
«THE WRITE HALF OF THE BANK IS GONE»): что отменено, что ратификация ОСТАВИЛА пер-термным
|
||||
(правку), и что под неё уже есть в схеме. ⚠ **Контрактная половина остаётся открытой** — канон
|
||||
0.4.0 несёт путь, три схемы и глагол, и это зона контрактной сессии.
|
||||
|
||||
Посылку промта пере-проверил САМ, чтением кода движка, а не по докам. Подтверждается вся:
|
||||
|
||||
| Факт | Улика |
|
||||
|---|---|
|
||||
| движок читает `mined_delta`, только если путь объявлен | `backend/internal/pipeline/mining.go` `loadMinedDelta`: `if r.Book.MinedDelta == "" { return nil, nil }` — ни одного чтения |
|
||||
| то же для `mined_rejects` | там же, `loadMinedRejects` |
|
||||
| объявленный ключ с НЕсуществующим файлом валит загрузку конфига целиком | `backend/internal/config/book.go:199-207`: `bad("mined_delta %s is not readable: %v")` |
|
||||
| ключей нет в шаблоне оператора | `backend/example/book.yaml` — ноль вхождений `mined_delta`/`mined_rejects` |
|
||||
| платформа пишет `book.yaml` ОДИН раз и фиксированным набором из пяти ключей | `platform/internal/books/render.go` — `O_EXCL`, «writes the file once and never again»; ключи `book_id · title · source_lang · target_lang · source_file` |
|
||||
| **новое, чего в промте не было:** декодер движка СТРОГИЙ — незнакомый ключ это жёсткая ошибка | `backend/internal/config/book.go:142-144`, `dec.KnownFields(true)` — то есть платформа не может «на всякий случай» дописать ключ, которого сборка движка не знает |
|
||||
| относительные пути резолвятся от каталога КНИГИ | `backend/internal/config/book.go:148-162`, `resolve()` — значит один шаблон на все книги даёт пер-книжные пути без подстановки |
|
||||
| форматы файлов | `mined_delta` = `seed.File` (`terms:` со схемой `internal/seed/seed.go:63-80`), грузится `membank.LoadGlossarySeed` и пере-штампуется `Source:"mined"`; `mined_rejects` = `rejects: [{src, note}]` (`mining.go` `minedRejectFile`), это ФИЛЬТР ПРЕДЛОЖЕНИЙ, в банк не входит |
|
||||
| на стороне платформы решения write-only | `internal/pgstore/readmodel.go` `SubmitBankDecisions` пишет `action`/`dst`; ни один SELECT их не читает, воркер описан комментарием собственной миграции `00002_readmodel.sql:172-174` и не построен |
|
||||
|
||||
**Варианты с ценой (решает владелец, не я):**
|
||||
|
||||
- **A. Платформа объявляет два ключа при рождении книги и сразу создаёт два пустых файла.**
|
||||
Работает без единой правки движка и без действий оператора; пустой файл законен (`terms:` пуст ⇒
|
||||
ноль записей, `os.Stat` проходит). **Цена:** платформа начинает знать два ключа СХЕМЫ движка, а
|
||||
форма Б ратифицирована ровно наоборот (D39.110 §2б: шаблон — деплой-артефакт оператора, платформа
|
||||
подставляет только личность, языки и имя файла). Плюс строгий декодер: сборка движка, которая
|
||||
этих ключей ещё/уже не знает, перестанет грузить КАЖДУЮ новую книгу.
|
||||
- **B. Ключи несёт шаблон оператора.** Платформа схемы не узнаёт, пути пер-книжные достаются даром.
|
||||
**Цена:** сам по себе вариант НЕ работает — движок отказывается грузить конфиг с объявленным, но
|
||||
отсутствующим файлом, а создавать файлы всё равно некому, кроме платформы. То есть B требует
|
||||
половины A или варианта D.
|
||||
- **C. Двигается ратифицированная граница владения:** платформе разрешено писать в каталог книги эти
|
||||
два файла (и только их), ключи несёт шаблон. **Цена:** правка ратификации D39.110; зато платформа
|
||||
по-прежнему не знает схемы — она пишет файлы по путям, которые прочитала из уже существующего
|
||||
`book.yaml`.
|
||||
- **D. Движок терпит объявленный, но отсутствующий `mined_delta`/`mined_rejects` как пустой.**
|
||||
Одна правка `backend/internal/config/book.go`, после чего работает чистый B: платформа пишет файлы
|
||||
только когда решения есть, и ничего не обязано существовать заранее. **Цена:** ослабляется
|
||||
намеренный гард — опечатка оператора в пути становится тихой. Смягчается тем, что послабление
|
||||
только для двух ключей и с громким WARN. Это строка ЕДИНОГО бэклога (движок), не моя.
|
||||
|
||||
**Рекомендация зоны: C или D+B.** Обе оставляют платформу в неведении о схеме движка — а это и есть
|
||||
то свойство, ради которого форма Б ратифицировалась. A решает быстрее всех и покупает это тем, что
|
||||
платформа впервые начинает знать схему чужой стороны, причём под строгим декодером.
|
||||
⚠ **Строить начну только после ратификации.** Границы, если разрешат, приняты как в §4.2: писать не
|
||||
«перед resume», а только на том резюме, которое СНИМАЕТ стоп подписи (`l.VerifyBank &&
|
||||
!l.BankReleased`); `--verify-bank` не трогать; пост-ридинговый цикл (строка 192) не проектировать.
|
||||
`PD-277` в регистре относил эту работу к отложенной 192. ⚠ **22.08 он ПЕРЕ-ДИСПОЗИРОВАН** — не по
|
||||
развилке, а по спилу глагола: остаток «честность `decline`» умер вместе с ним. Ниже — прежняя
|
||||
формулировка обязательства, оставленная как след решения: правлю его только после ратификации, как
|
||||
промт и велит.
|
||||
|
||||
### Как это проверялось
|
||||
|
||||
**Батарея:** `cd platform && make check` с ОБОИМИ гейтами (`TM_PLATFORM_TEST_DSN` на живом
|
||||
PostgreSQL 18.4, `TM_PLATFORM_TEST_ENGINE_BIN` + `_BOOK_TEMPLATE`) — 18 пакетов, EXIT=0, скипов 0,
|
||||
линтер «0 issues». Дифф имён тестов — исполнением: `grep -rho '^func Test[A-Za-z0-9_]*'` по дереву
|
||||
против `git show HEAD:<файл>` по всем `_test.go`. ⚠ **Замер пере-снят при сдаче** и его исход
|
||||
изменился: `comm -23` даёт ТРИ удалённых имени — все три описывали пер-термную подпись банка,
|
||||
снятую 22.08 по слову владельца (`PD-370`, разбор в шапке), — а `comm -13` даёт 29 новых.
|
||||
Прежняя формулировка «удалённых нет» была верна до спила и снята, а не оставлена рядом с новой.
|
||||
|
||||
**Посадки мутаций волны 1 — 20, поймано 20** (перечень ниже; счёт пере-снят при сдаче — прежняя
|
||||
цифра «16» не сходилась с собственным списком в той же фразе). Каждая ставилась в код, пин обязан был упасть, код возвращался:
|
||||
снятие `payload["revision"]` ✅ · снятие `payload["structure_version"]` ✅ · `ContractVersion` назад
|
||||
на 0.3.0 ✅ · `inReadTx` без обеих опций ✅ · без изоляции ✅ · без read-only ✅ · `ListBooks` на
|
||||
`inTx` ✅ · снятие `and book_id <> $2` ✅ · снятие пола самоописания манифеста ✅ · снятие пола «ноль
|
||||
глав» ✅ · обе фазы свипа на ОДНОМ дедлайне ✅ · ранний возврат фазы из `Sweep` ✅ · свип читает
|
||||
`ListLiveRuns` вместо списка «пора» ✅ · отсрочка только по ошибке ✅ · `DeferReadModelDebt` в
|
||||
`now()` ✅ · долг никогда не списывается ✅ · новая граница наследует потраченный бюджет ✅ · колонка,
|
||||
которой нет в схеме, в СКЛЕЕННОМ фрагменте (гейт SQL) ✅ · то же в локальной `const q` ✅ ·
|
||||
несворачиваемый SQL ✅.
|
||||
⚠ **Три посадки из этого списка сперва ПЕРЕЖИЛИ** мои же пины и вскрыли настоящие дефекты пака —
|
||||
разбор в `PD-337`/`PD-338`/`PD-339`; пины переписаны, мутации пере-посажены.
|
||||
|
||||
**Воспроизведение ПЕРЕД пином — там, где промт этого требовал (пп. 2, 12, 13):** три мутации
|
||||
посажены разом в HEAD-код и прогнана ПОЛНАЯ батарея — 18 пакетов, exit 0, скипов 0. То есть все три
|
||||
дыры подтверждены исполнением, а не прочитаны в пинге.
|
||||
|
||||
**Живые пробы — настоящий `tmctl`, живой Postgres, дев-демон на 127.0.0.1:8098:**
|
||||
- `GET /v0/capabilities` → `"contract_version": "0.4.0"` (пункт 1 на проводе);
|
||||
- `tmplatformctl seed` — аккаунт, грант, книга через ЖИВОЙ `POST /v0/books`: интейк прошёл, движок
|
||||
нарезал 3 главы, `GET /chapters` отдаёт 3, долг материализации погашен, `read_model_attempts = 0`.
|
||||
**Это отрицательная проверка нового пола: законный манифест живого движка он НЕ отвергает;**
|
||||
- **PD-162 воспроизведён живьём** — каталог книги унесён, прогон принят (202), спавн падает.
|
||||
`tmplatformctl runs` печатает строку с числом неудач `1`, временем следующей попытки, суммой холда
|
||||
`0.090000`, его возрастом, `(none)` вместо юнита и текстом ошибки движка. **До этого пака оператор
|
||||
видел только растущий счётчик `sweep_unfinished_total` и не мог сделать ничего;**
|
||||
- `run abandon` без флага: прогон `failed`/`service_error`, деньги не тронуты, книга РАЗМОРОЖЕНА —
|
||||
следующий прогон принят (202). С флагом `--release-hold`: холд вернулся целиком;
|
||||
- **деньги пере-считаны из сырого леджера двумя путями и сошлись до цента:** кэш
|
||||
`account_balances.balance_micro_usd` = 5000000, `sum(amount_micro_usd)` по `credit_ledger` =
|
||||
5000000, открытых холдов 0; строки леджера — `grant · hold · hold_release · hold · hold_release`;
|
||||
- метрики на проводе: `tm_platform_runs_stalled 0`, `tm_platform_reading_surfaces_abandoned 0`;
|
||||
- обе новые ручки конфигурации печатаются на буте с источником (`TM_PLATFORM_SWEEP_BUDGET` `2m0s`
|
||||
`default`, `TM_PLATFORM_RUN_BUDGET` `1m0s` `default`) — норма PD-114.
|
||||
|
||||
**Гейты доков:** `python3 docs/scripts/counts.py --check` — exit 0, литералы сходятся;
|
||||
`--lint` — exit 0.
|
||||
⚠ **Раскрываю правку ЧУЖОГО документа:** пак убил два якоря в `PLATFORM_P8_REVIEW_SESSION_PROMPT.md`
|
||||
(константы `sweepBudget`/`defaultRunBudget` переехали), и `--lint` был из-за этого КРАСНЫМ. Якоря
|
||||
пере-нацелены на новые места, абзац оставлен как описание НАЙДЕННОГО, а не текущего кода. Правил
|
||||
чужой промт сознательно и говорю об этом прямо: якоря умерли по моей вине, а красный гейт не
|
||||
обходят. Оркестратор вправе откатить эту правку — содержательного текста она не трогает.
|
||||
⚠ Первая моя проверка этого гейта дала EXIT=0 и была ЛОЖНОЙ: я мерил в момент, когда файла с
|
||||
якорями в дереве не было, поэтому линт его не сканировал. Критик полноты был прав, я нет.
|
||||
|
||||
### Спил пер-термной подписи банка (слово владельца 22.08) и что осталось контракту
|
||||
|
||||
**Заказ:** модель отменена `D39.144` ещё 16.08, реализации нет — спилить в зоне, оставить пометку
|
||||
будущим сессиям. Перед спилом — аудит воркфлоу: 5 линз (канон · код зоны · потребители ·
|
||||
ратификация · будущая пометка) + адверсариальная проверка каждого рискованного вывода, **31 агент**.
|
||||
|
||||
**Снято из зоны:** маршрут `POST /books/{bookId}/bank/decisions` · хендлер `bankDecisions` и
|
||||
wire-типы (`wireBankDecision`/`wireBankDecisions`/`wireBankReceipt`) · метод `SubmitBankDecisions`
|
||||
из интерфейса `Library` и из стора · типы `BankDecision`/`BankReceipt` · `ErrUnknownTerm` /
|
||||
`UnknownTermError` и их отображение в контрактный код · пины, которые их описывали.
|
||||
|
||||
**НЕ тронуто, и это проверено:** чтение банка (`GET /bank`, `BankTerm`, оси статуса и происхождения)
|
||||
· вокабула ДВИЖКА `proposed|in_progress|approved` в сайдкар-инжесте — она чужая и не отменялась ·
|
||||
сам стоп `awaiting_bank` и подпись как `resume` (D39.144 снимала ГЕЙТ, не СТОП) · таблица
|
||||
`bank_decisions` и её констрейнты: миграции релизные, а колонки `term_id`/`dst` — ровно то, что
|
||||
нужно будущей ПРАВКЕ термина. Данных под снос не было: на стенде ноль строк решений.
|
||||
|
||||
**Пометка на месте write-пути** (`internal/httpapi/reading.go`, «THE WRITE HALF OF THE BANK IS
|
||||
GONE»): что именно отменено, что ратификация ОСТАВИЛА пер-термным (правку и добавление термина),
|
||||
чего нет ни на одной поверхности, и что под это уже есть в схеме и в движке (`mined_delta`).
|
||||
|
||||
⚠ **Гейт размера поверхности сработал** — `len(contractSurface) < 15` поймал снятие маршрута. Пол
|
||||
сдвинут 15 → 14 **с причиной в комментарии**: пол, опущенный под ратифицированное сокращение, — не
|
||||
подгонка; опущенный без неё — подгонка. Два теста, стоявшие на снятой ручке как на «любом POST»,
|
||||
пере-нацелены на живой маршрут, а не удалены.
|
||||
|
||||
#### Насколько страдает контракт (главный вопрос владельца)
|
||||
|
||||
Канон **не лжёт про модель** — `D39.144` в него внесена и записана в четырёх местах верно
|
||||
(`openapi.yaml:443-448`, `:539-541`, `:707`, `:2498-2499`). Беда в другом: **рядом с новой моделью
|
||||
в полном рабочем обмундировании стоит старая** — путь, три схемы, четыре счётчика, — и обе зовутся
|
||||
словом «signing». Файл несёт ДВЕ модели подписи.
|
||||
|
||||
- **Прямая ложь — шесть мест** (четыре в каноне, два в компаньоне). Худшие: `:1830-1832`
|
||||
(«статус подтянется на следующей пересборке» — не подтянется, решения до движка не доезжают, что
|
||||
говорит сам же канон на `:1959-1963`) и компаньон `:672` («`POST /bank/decisions` НЕ ПОСТРОЕНО» —
|
||||
было построено в P7 и снято сегодня).
|
||||
- **Самопротиворечия — три.** Самое явное: `:443` «Signing the bank is ONE act over the whole of
|
||||
it» против `:477` «Signing is not a row edit» — заголовок описания ручки называет подписью САМУ
|
||||
РУЧКУ. Тридцать четыре строки между ними, и оба текста компилируются в сгенерированный клиент.
|
||||
Плюс `:1918-1922` («строка может быть решена и НЕ подписана, потому что отказ — тоже решение») —
|
||||
дословно отменённая модель — соседствует с `:1929-1932` («INFORMATIONAL, never a gate»).
|
||||
- **Дыра, которую спил вскрывает и которую надо назвать вслух:** `approve` + непустой `dst` был
|
||||
ЕДИНСТВЕННЫМ живым носителем того, что D39.144 оставила пер-термным — «поправить термин». А
|
||||
«добавить свой термин» модель называет, и в контракте его **нет вообще**: на 17 путей канона
|
||||
запись в банк была ровно одна. То есть после спила у собственной фразы канона `:539-541`
|
||||
(«correct what they care to, then release it with ONE resume») **нет ручки**.
|
||||
- **Счётчики вырождаются.** `pending_decisions` считается как «строки `proposed`, на которые НЕТ
|
||||
записи в `bank_decisions`»; писателя больше нет ⇒ это «сколько строк предложено», а `complete`
|
||||
истинно ровно тогда, когда read-out движка не несёт ни одной `proposed` (не константа: сайдкар
|
||||
несёт и `in_progress`, и `approved`). Поля обязательные на проводе (`EventBank.required`), поэтому оставлены как есть:
|
||||
менять их смысл — правка канона, а не зоны.
|
||||
- **Объём и вес:** ~17 мест в `openapi.yaml` (~150 строк из 2592) + ~8 в компаньоне. Вес —
|
||||
**ломающий минор 0.4.0 → 0.5.0**; мажора нет ни по политике («0.x ломает миноры весь бета-период»),
|
||||
ни по факту. Цена фронту нулевая ДОПОЛНИТЕЛЬНО: его зеркало ратифицированно стоит на 0.2.3.
|
||||
|
||||
**Пока канон не поправлен, деплой обслуживает на одну ручку меньше, чем объявляет.** Это записано
|
||||
строкой `PD-370` (зонная половина закрыта, контрактная открыта), и это осознанная цена спила, а не
|
||||
недосмотр.
|
||||
|
||||
### Волна 2: воркфлоу-ревью и что оно нашло в САМОМ паке
|
||||
|
||||
Заказ владельца: после возврата ревьюеров волны 1 и починки их находок — воркфлоу пошире, 10–30
|
||||
агентов, один Fable 5 на тяжелейший участок. Исполнено: **11 линз поиска + адверсариальный рефутер
|
||||
на КАЖДУЮ находку + синтез, 47 агентов, 0 ошибок**. Fable 5 — один, на свипе и деньгах.
|
||||
|
||||
**36 находок, 21 пережила рефутера, 15 опровергнуто.** После склейки дублей (голодовку расчётной
|
||||
фазы назвали пять линз, срез по байту — две) это **17 строк регистра**: `PD-353…PD-366` закрыты,
|
||||
`PD-367`/`PD-368`/`PD-369` открыты — они не мои решения и переданы как вопросы.
|
||||
|
||||
⚠ **Итоговая строка самого воркфлоу («ни одна находка не пережила проверку») — БАГ МОЕГО скрипта
|
||||
синтеза, а не вердикт.** Она противоречила сама себе (пустыми были и «выжившие», и «убитые»), и
|
||||
таблица пере-собрана из `journal.jsonl` — по одной записи на агента. Говорю прямо: доверься я
|
||||
сводке, пак уехал бы на приёмку с блокером внутри.
|
||||
|
||||
**Блокер, найденный ПЯТЬЮ независимыми линзами (включая Fable 5) и подтверждённый мной по коду
|
||||
до их вердикта — `PD-353`.** Пак разделил проход на фазы, чтобы одна не могла съесть другую, и
|
||||
оставил ВНУТРИ второй фазы то же самое голодание: `UnsettledRuns` упорядочен старейшим-первым,
|
||||
каждый элемент стоит целый бюджет, а `settle` при нечитаемой трате возвращает nil — то есть клин
|
||||
рапортует УСПЕХ, потратив проход. Одна недоступная книга замораживала холды ВСЕХ аккаунтов, каждый
|
||||
проход. Это ровно та формулировка, которую шапка `Sweep` объявляла побеждённой.
|
||||
|
||||
**Ещё два, которые делали механизмы пака недостижимыми:**
|
||||
- `PD-354` — счётчик неудач обнулялся ТИШИНОЙ: дорогой вопрос движку задаётся раз в `ResyncEvery`,
|
||||
остальные проходы ничего не устанавливают, и `ClearRunDeferral` на них сбрасывал счёт. 1,0,1,0 —
|
||||
`StalledAfter` недостижим, гейдж пуст, оператору не говорят никогда.
|
||||
- `PD-355` — колонка SPENT в операторской таблице показывала пожизненную трату КНИГИ. Моя же
|
||||
починка G8 волны 1 взяла не то число, на единственной таблице, заведённой ради решения о деньгах.
|
||||
|
||||
**Чему меня научили РЕФУТЕРЫ (15 опровержений, каждое куплено исполнением, а не рассуждением):**
|
||||
- «Поднять `RunBudget` = объявить здоровый прогон застрявшим» — **опровергнуто замером**: поднятие
|
||||
ручки не меняет вообще ничего, а строгая проверка `RunBudget < SweepBudget/2` отвергла бы сами
|
||||
шиппящиеся дефолты. Я уже держал эту «починку» в плане и не построил её. Остаток — `PD-368`.
|
||||
- «Интейк обязан применить `Whole()`» — **опровергнуто контрактом и батареей**: вся батарея интейка
|
||||
ездит на манифестах без списка глав, контракт интейка — только счётчики. Механика находки верна
|
||||
(`PD-367` открыт), лекарство — нет.
|
||||
- «Пропуск тела помощника в гейте SQL = дыра» — **опровергнуто исполнением**: мутация в теле `bump`
|
||||
краснит десятки поведенческих тестов; спрятать там непроверяемый оператор нельзя.
|
||||
- «Стойл-детектор слеп к форме PD-162» (находка Fable 5) — опровергнуто.
|
||||
|
||||
**Две линзы разошлись во ВЕСЕ одной находки** (`PD-358`, срез по байту в операторской таблице):
|
||||
одна назвала микрополировкой вне рамки, вторая довела цепочку до многобайтового stderr движка.
|
||||
Починил как дешёвое и бесспорное, расхождение раскрыл в строке.
|
||||
|
||||
### Самопроверка (артефакт §5: имена агентов и по одной команде или цитате из каждого)
|
||||
|
||||
Исходов три, «не проверено» ≠ «опровергнуто». Каждому агенту выдан СВОЙ каталог песочницы явным
|
||||
путём, мандат читающий, state-changing git запрещён перечислением (D39.113). В рабочее дерево не
|
||||
писал никто: оба ревьюера волны 1 ответили на прямой вопрос уликами.
|
||||
|
||||
**Волна 1 — три изолированных ревьюера по финальному диффу (author≠reviewer):**
|
||||
|
||||
| Агент | Ось | Цитата или команда ИЗ ЕГО отчёта | Находок | Исход |
|
||||
|---|---|---|---|---|
|
||||
| `agent-afec473415b228cdf` | контракт-конформность против канона 0.4.0 + свои посадки | «в песочнице создан симлинк `<scratch>/docs -> …/docs` — новый гейт `internal/gates/contract_test.go` тянется за канон ЗА пределы модуля, и без него копия дерева его не находит» | 5 + 3 пережившие мутации | все 8 починены |
|
||||
| `agent-a394edded388676bc` | шов с движком и деньги, свой инвентарь каналов | «`repo*/platform/cmd/rev2probe\|rev2ctl\|rev2seam\|rev2ceiling\|rev2ids/main.go` и `repo*/platform/internal/runs/rev2_starve_test.go` — мои пробы (только в копиях)» | 8 + 3 пережившие мутации | все 11 починены; G1 — БЛОКЕР в моём коде |
|
||||
| `agent-a1e4407d89e52128d` | полнота сдачи: пункт заказа → носитель в дереве | «Snapshot taken at 2026-08-22 00:49:24. ⚠ The tree was moving while I read it: `platform/BACKLOG.md` gained rows П-19/П-20 at 00:48, mid-review» | 6 | 5 починены, 1 (К-4) вскрыла МОЮ ошибку измерения |
|
||||
|
||||
Две разведки (read-only, не ревью): `agent-acf370d564663617e` — инвентарь SQL для границы sqlc;
|
||||
`agent-a1cb3d1a5c59f2f2e` — инвентарь каналов движка.
|
||||
|
||||
**Волна 2 — воркфлоу `wf_f8d1f3e6-85a`, 11 линз (одна на Fable 5) + рефутер на каждую находку:**
|
||||
|
||||
| Линза (агент) | Предмет | Цитата или команда ИЗ ЕГО отчёта | Находок | Пережило |
|
||||
|---|---|---|---|---|
|
||||
| `a2fdcd13144058b57` **Fable 5** | свип и деньги — тяжелейший участок | «Стойл-детектор слеп к клину „юнит исчез, workdir недоступен“ (форма PD-162): реконсиляция каждый проход рапортует успех» | 3 | 2 |
|
||||
| `ae8f856fe08ee9e61` | фазы и отсрочка свипа | «Счётчик отсрочек не может дорасти до StalledAfter для того самого клина, ради которого написан» | 4 | 3 |
|
||||
| `a3c4dc82a7d62f8a6` | архитектурный стиль, велосипеды | «Фаза расчёта денег осталась без отсрочки: head-of-line starvation, от которой пак и построен, на списке `UnsettledRuns` не починена» | 3 | 1 |
|
||||
| `a62786a00eb5bbae4` | каноны Go и каноны зоны | «RunBudget молча обрезан половиной SweepBudget» (опровергнуто) + голодовка расчётной фазы | 4 | 2 |
|
||||
| `af8620a80b7e12b06` | материализация и пол манифеста | «Провалившийся экспорт на смене кадра затирает весь оплаченный текст книги» | 3 | 3 |
|
||||
| `ada2420ac7c576856` | CLI, метрики, конфиг | «SPENT в операторской таблице — пожизненный счётчик КНИГИ, а не траты этой попытки» | 3 | 3 |
|
||||
| `ac92e0a8acba148ec` | сам гейт SQL: даёт ли ложную зелень | «`unresolvable` ключёвана функцией, а не сайтом: второй несворачиваемый запрос в той же функции молча проверяется текстом первого» | 2 | 1 |
|
||||
| `a21f43459f3f12f7e` | комментарии: вода против несущего | «perf_test.go копирует „нормативные“ цифры из миграции 00022 и в той же фразе утверждает, что не копирует» | 4 | 1 |
|
||||
| `a8faf63f1dde50903` | скоуп: сверка построенного против БУКВЫ промта | «Пункт 9: новое условие замера в perf_test.go ложно для бенчмарка этого же файла» | 4 | 2 |
|
||||
| `a4dad647c0eb8310f` | регрессии: что пак сломал в уже работавшем | «Отмена контекста сообщается как дедлайн: остановка демона посреди прохода поднимает счётчик голодания» | 3 | 2 |
|
||||
| `a178aca863af19915` | контракт-конформность построенного против канона 0.4.0 | «Пункт 1: подъём константы оставил в коде утверждение, что деплой объявляет 0.3.0» (опровергнуто) | 3 | 1 |
|
||||
|
||||
Рефутеры (по одному на находку, 36 штук) считаются наравне с находками — три примера того, чем
|
||||
куплены их вердикты:
|
||||
- `ad12d9bcca05b883a`: «`WITH the index n_tup_upd=3942 n_tup_hot_upd=3909 (HOT 99.2%) idx size=8192`
|
||||
против `WITHOUT the index n_tup_upd=1994 n_tup_hot_upd=1984 (HOT 99.5%)`» — из этого замера
|
||||
выросла `PD-364` (индекс удалён);
|
||||
- `a337318688be20d30`: «`finished_at = now()` → `books.go:313: column "finished_at" of relation
|
||||
"books" does not exist (42703)` … на всех 5 сайтах»;
|
||||
- `a7fadd3c541e9a87d`: «пять проходов подряд дают „the sweep settled 2 of 3 finished runs: context
|
||||
deadline exceeded“, третий, чистый прогон не достигается никогда, reserved держится на $0.90» —
|
||||
это подтверждение блокера `PD-353` исполнением.
|
||||
|
||||
**Посадки мутаций волны 2 — 6, поймано 6:** фаза расчёта не отсрочивает ✅ · `UnsettledRuns`
|
||||
игнорирует отсрочку ✅ · тишина снова считается здоровьем ✅ · `RunBudget` снова не присваивается ✅ ·
|
||||
срез по байту в операторской таблице ✅ · управляющие символы не вычищаются ✅.
|
||||
⚠ Две первые попытки посадить последние две дали НЕ падение теста, а ошибку сборки (осиротевший
|
||||
импорт) — это слабее, и они пере-посажены компилирующимися.
|
||||
|
||||
### Релей §4.7: не приходил
|
||||
|
||||
Аддендум пака P8-REVIEW должен был доехать **только релеем оркестратора с эхо-подтверждением**.
|
||||
За сессию релея не поступало: ни находок по четырём осям, ни эхо. Отчёт читающего пака я не читал
|
||||
и не гадал по нему — промт это прямо запрещает, состав пака определяет оркестратор.
|
||||
|
||||
Единственное касание того пака — техническое и раскрыто выше: два его якоря умерли от МОИХ правок
|
||||
кода, `--lint` покраснел, якоря пере-нацелены (`docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md:62`,
|
||||
плюс третье пере-нацеливание 22.08 после спила). Содержательного текста чужого промта не трогал.
|
||||
|
||||
### Obstacle: что не удалось и что не проверено
|
||||
|
||||
- **Сквозного живого перевода через платформу не было** и в этом паке быть не могло: строка 202
|
||||
гейчена холодным прогоном движка (решение владельца 20.08). Всё после «прогон стартовал и
|
||||
остановился» доказано батареей против живого Postgres и настоящего `tmctl`, а не переводом.
|
||||
- **Труба 199(а) не построена и форматы сверены ЧТЕНИЕМ**, а не записью файла: `mined_delta` и
|
||||
`mined_rejects` прочитаны в коде движка (`membank.LoadGlossarySeed`, `mining.go`), но ни одного
|
||||
файла платформа не писала — писать было бы строительством нератифицированной развилки.
|
||||
- **Замер `PD-297` не снят.** Отказ осознанный: путь самый опасный на запись, править его надо с
|
||||
замером на корпусной книге до и после, а корпусной книги в паке не было.
|
||||
- **Финальный дифф ПОСМОТРЕН** независимым ревьюером (author≠reviewer) уже после спила — он и
|
||||
закрыл эту строку obstacle. Три его major были настоящими дырами в МОЁМ коде, и все три —
|
||||
отсутствие пина, а не ошибка логики: пол `ErrTextUnknownForANewCut` (охраняет единственную
|
||||
запись, способную стереть оплаченный текст) · «стоп перевешивает отсрочку» в `RunsToReconcile` ·
|
||||
«пересборка банка не трогает `bank_decisions`» (свойство, на которое опирается пометка, ради
|
||||
которой спил и делался). Каждая закрыта пином, каждый пин проверен посадкой мутации. Плюс семь
|
||||
минорных: комментарии `SaveBank` и `bankCountsTx` описывали отменённую модель как живую,
|
||||
пометка врала про `action NOT NULL`, снятие NUL не было запинено. Не найдено дефектов там, где
|
||||
он искал отдельно: пол поверхности 15→14 равен реальному размеру таблицы, пере-нацеленные тесты
|
||||
проверяют то же свойство, лишнего не срезано.
|
||||
- **Число скипов батареи** снято рецептом `make check`, который пишет `.check.log` в дерево; при
|
||||
параллельных прогонах агентов лог обрезался. Отдельная сверка двух пакетов с условными скипами
|
||||
(`internal/runner`, `internal/gates`) дала `--- SKIP` = 0.
|
||||
- **Контрактная половина `PD-370` открыта**: пока канон не поправлен, деплой обслуживает на одну
|
||||
ручку меньше, чем объявляет. Это цена спила, названная заранее, а не обнаруженная при приёмке.
|
||||
|
||||
### Инвентарь каналов движка (артефакт §4.4 — собран ЧТЕНИЕМ кода движка, не по нашим докам)
|
||||
|
||||
Пак коснулся канала движка (пункт 10: `ingest.Manifest.Whole()` построен как зеркало движкового
|
||||
правила), поэтому инвентарь сдаётся таблицей, а не словами «я прочитал код движка».
|
||||
|
||||
| Канал | Писатель в движке | Атомарность | Читатель/писатель платформы | Согласовано? |
|
||||
|---|---|---|---|---|
|
||||
| `<project_db>.manifest.json` | `pipeline/manifest.go:379` `writeFileAtomic` | **атомарно** (temp+`Sync`+rename, `pipeline/artifact.go:24-59`) | `internal/runner/engine.go` (`tmctl manifest`) → `ingest.DecodeManifest` → `internal/books/parse.go`, `internal/readmodel` | ✅ **и стало строже**: `Manifest.Whole()` теперь зеркалит `BookManifest.selfConsistent` (`pipeline/manifest.go:460-483`) поле в поле по тем правилам, которые платформа вообще читает (главы, юниты, нумерация). Верхняя граница чанков движка у нас counterpart'а не имеет намеренно — платформа счётчиков чанков не берёт |
|
||||
| `<project_db>.bank.json` | `pipeline/bankexport.go:154` `writeFileAtomic` | **атомарно** | `internal/runner/artifacts.go:37` → `ingest.DecodeBank` → `pgstore.SaveBank` | ✅ |
|
||||
| `<project_db>.bank-stop.json` | `pipeline/mining.go:393` `writeFileAtomic` | **атомарно** | **читателя НЕТ** (грепом по зоне — ноль вхождений) | ⚠ канал существует и не потребляется; полная таблица подписи платформе сегодня не нужна, она проецирует банк |
|
||||
| `<project_db>.bank-stop.txt` | `pipeline/mining.go:195` `os.WriteFile` | **НЕ атомарно** (усечение первым делом) | читателя нет | ✅ и это важно: брать его на живом прогоне нельзя — исправление пинга №15 оркестратором подтверждается кодом |
|
||||
| `<project_db>.mined-signature.yaml` | `pipeline/mining.go:188` `os.WriteFile` | **НЕ атомарно** | читателя нет | ✅ то же |
|
||||
| `<project_db>.auto-bank.yaml` | `pipeline/mining.go:741` `os.WriteFile` | **НЕ атомарно** | читателя нет | ✅ то же |
|
||||
| `mined_delta` (путь из `book.yaml`) | **писателя в движке НЕТ** — только читатель `pipeline/mining.go` `loadMinedDelta`; формат `seed.File` (`internal/seed/seed.go:63-80`), грузится `membank.LoadGlossarySeed`, `Source` пере-штампуется на `"mined"` | — | **писателя НЕТ и у платформы** | ❌ **разрыв — это и есть строка 199(а)** |
|
||||
| `mined_rejects` | читатель `pipeline/mining.go` `loadMinedRejects`; формат `rejects: [{src, note}]`; ФИЛЬТР ПРЕДЛОЖЕНИЙ, в банк не входит | — | писателя нет | ❌ тот же разрыв |
|
||||
| `events.jsonl` (NDJSON эмиттера) | движок, StreamVersion 1.1 | append-only | `internal/ingest/tail.go` + `pgstore.RunSink` | ✅ пак не трогал |
|
||||
| exit-коды `tmctl` | контракт движка | — | `ingest.OutcomeOf`, `internal/runs/reconcile.go` `outcome` | ✅ пак не трогал |
|
||||
| `tmctl status --json` | движок | — | `internal/runner/engine.go` → `ingest.StatusReport`; расчёт денег | ✅ пак не менял форму; изменил лишь то, СКОЛЬКО раз его зовут при неудаче (бэкофф) |
|
||||
| `book.yaml` | оператор (шаблон) + **платформа один раз** (`internal/books/render.go`, `O_EXCL`) | — | платформа ПИШЕТ пять фиксированных ключей | ⚠ **декодер движка СТРОГИЙ** (`config/book.go:142-144`, `KnownFields(true)`): незнакомый ключ = жёсткая ошибка. Значит любое расширение набора ключей платформой — ратификация, а не правка. Прямо относится к развилке 199(а) выше |
|
||||
|
||||
- **P7, акт 5 — ЗАКРЫТ. Дерево готово к приёмке и лендингу.**
|
||||
Батарея: 18 пакетов, exit 0, скипов 0, линтер 0 issues, тестовых функций **532** (⚠ было «533» — испр. оркестратором №18 20.08: канонической командой D39.121 `grep -rh '^func Test' platform --include='*_test.go' | wc -l` выходит 532; расхождение на единицу, ни на что не влияет, но число было заявлено без команды). Регистр — 327
|
||||
строк, открытых 65 (13 minor, 52 info), **major и BLOCKER нет**. Дерево не закоммичено и не
|
||||
застейджено. Миграции пака: **00019–00024**.
|
||||
|
||||
### Что построено и зачем
|
||||
|
||||
1. **Долг на материализацию — колонка с арендой** (`books.read_model_owed_at`, 00019).
|
||||
Ставится в ТОЙ ЖЕ транзакции, что закрывает границу работы (конец разбора и все три конца
|
||||
прогона), гасится сверкой метки, берётся в аренду тем, кто платит. **Зачем:** очередь жила в
|
||||
памяти процесса — упавший `Refresh` её терял, рестарт демона терял её всю, и оплаченный текст не
|
||||
доезжал до читателя НИКОГДА. Плюс «в покое» для потока теперь значит «и материализация не
|
||||
должна» — это Ф-56: книга, только что нарезанная, получала `end` и 204 «не переподключайся»
|
||||
ровно в те секунды, пока дерево ещё не легло.
|
||||
**Почему отошёл от прежней реализации:** удалены очередь в памяти (`runs.deferRefresh`/
|
||||
`DrainRefresh`/`Reader`) и бэкстоп «дерева нет вовсе» (`books.materializeMissingTrees`,
|
||||
`pgstore.BooksWithNoTree`) — оба покрывались одним механизмом, а бэкстоп к тому же не видел
|
||||
книгу, у которой дерево есть, но на границу старше. Дрейн переехал в `readmodel.Drain` — в
|
||||
пакет, чья это работа.
|
||||
2. **«Глава сделана» = последний проход, который книга на ЭТОМ деплое реально получает**
|
||||
(`books.edit_wave`, 00024). **Зачем:** счёт шёл по волне `edit`, и на деплое без редактора ни
|
||||
одна глава не была сделана никогда — шкала покупки не подрезалась, сервис бесконечно предлагал
|
||||
купить уже переведённое. Форму объявляет сам движок (пайплайн без редактора даёт волне `edit`
|
||||
знаменатель ноль). **Почему на КНИГЕ, а не на прогоне:** прочитанная с последнего прогона, она
|
||||
роняла `chapters_done` в ноль в момент допуска нового прогона — счётчик ходил назад, что канон
|
||||
запрещает. Флаг монотонный: книга, прошедшая редактирующий пайплайн, остаётся такой.
|
||||
3. **У клейма идемпотентности появился владелец** (`claim_token`, 00020) и **тождество интейка
|
||||
решается дайджестом файла** (`content_sha256`, 00023). **Зачем:** строка ключа не несла, какая
|
||||
попытка её держит — застрявшая попытка удаляла строку преемника (пользователь получал вторую
|
||||
книгу) или писала свою квитанцию поверх чужой (клиент реплеил `Location` несуществующей книги);
|
||||
а тождество по `Content-Length` не различало две РАЗНЫЕ книги с одинаковым объявлением.
|
||||
4. **Остановка АККАУНТА читается с аккаунта**, а не с потолка одного прогона; `resume` различает
|
||||
«прогон закончен, нужен новый» (`ceiling_reached`) и «пополните счёт» (`credit_unavailable`);
|
||||
`blocked` называет холд, который реально освободит шкалу. **Зачем:** всё это — «у вас нет
|
||||
денег» пользователю, у которого деньги есть, и отправка его делать не то.
|
||||
5. **Счётчик замечаний вернулся на главу** (`chapters.note_count`, 00022), `chapters.units_done`
|
||||
снесён (00021). **Почему это разворот решения 00016 того же пака:** 00016 снёс колонку с
|
||||
доводом «её никто не пишет» — здесь она возвращается ВМЕСТЕ с писателем, которого ей не
|
||||
хватало. Основание замерено: джойн, которым её заменяли, стоил 83% времени страницы библиотеки
|
||||
(16.8 мс против 3.4 мс на корпусе 40 книг × 500 глав, ВАКУУМИРОВАННОМ); со счётчиком — 6.6 мс.
|
||||
⚠ Условие замера названо здесь и при каждом другом числе про него (P8-FIX, PD-336): на ХОЛОДНОМ
|
||||
корпусе тот же джойн даёт 96%, и это не противоречие, а другой замер. Нормативный носитель
|
||||
вакуумированных цифр — миграция 00022, остальные на неё указывают. Бенчмарк оставлен
|
||||
в дереве: `go test ./internal/pgstore/ -bench LibraryPage -run xxx`.
|
||||
|
||||
### Что форсировано и почему
|
||||
|
||||
- **Работа по канону 0.4.0 лежит в дереве до его ратификации.** Основание: ответ контрактной
|
||||
сессии на записку зоны плюс слово владельца 20.08 про «кончились деньги → сообщить и дать
|
||||
продолжить». Ломающая правка ровно одна — `Run.stop_requested` (0.3.0 такого поля не объявляет);
|
||||
`cause.code: credit_unavailable` легален и по 0.3.0, второй уровень там открыт. Зависимость
|
||||
записана строкой **PD-327** и закрывается лендингом канона, а не работой зоны. Если форма
|
||||
сдвинется словом владельца — правки отделимы, карта в `archive/P7_ACT5_FIX_PLAN_2026-08-20.md` §5б.
|
||||
- **Тесты на АТОМАРНОСТЬ сверяют `xmin`** (транзакцию, последней писавшей строку). Это не
|
||||
экзотика ради экзотики: два пина акта проходили под мутацией «вынести метку из транзакции во
|
||||
второй оператор» — конечное состояние она не меняет, меняет окно, и утверждать про окно иначе
|
||||
тест не может.
|
||||
|
||||
### Что осталось, названо и не спрятано
|
||||
|
||||
- **PD-327** — ждёт лендинга канона 0.4.0 (не работа зоны).
|
||||
- **PD-297** — round-trip на строку в `SaveStructure` под эксклюзивной блокировкой книги. Не взят
|
||||
осознанно: путь самый опасный на запись, инструмент штатный (`tx.SendBatch`), но правку надо
|
||||
мерить на корпусной книге до и после.
|
||||
- **Четыре оси не смотрел никто и никогда:** деньги и леджер целиком · вход/сессии/CSRF · очередь
|
||||
и джобы · метрики. Пак их не менял, а все приёмки смотрели дифф. Это отдельный пак.
|
||||
- **Живого перевода на стенде не было ни разу** — движку нужны ключи провайдера и деньги. Всё
|
||||
после «прогон стартовал и остановился» (подпись банка, halt на потолке, резюм после пополнения)
|
||||
покрыто батареей против живого Postgres, а не живым движком.
|
||||
|
||||
### Как это проверялось
|
||||
|
||||
`make check` с гейтами (скипов 0) · каждая правка проверена ПОСАДКОЙ МУТАЦИИ: код испорчен именно
|
||||
так, как называет комментарий пина, пин обязан упасть, код возвращён (40 проб, все поймались) ·
|
||||
живые пробы по ручкам на стенде: интейк и реплей, `409 key_reused` на другой файл под тем же
|
||||
ключом, часть после файла на реплее, `/chapters` `/units` `/notes` `/bank`, ETag→304 и разные теги
|
||||
у страниц, 410 на чужой id главы, `after_version` из прошлой жизни, `unsupported_pair`, `/usage`
|
||||
на пустом счету и после `adjust`, старт и стоп прогона со `stop_requested` на проводе, SSE в
|
||||
покое и SSE с непогашенным долгом (Ф-56 на проводе), 400 вместо 413 на длинном теле.
|
||||
|
||||
### Самопроверка
|
||||
|
||||
Акт закрыт адверсариальным ревью СОБСТВЕННОГО диффа (9 линз × 2 рефутера + критик полноты, 84
|
||||
агента, 0 ошибок): **37 находок, 18 пережили рефутеров**, и почти все — дефекты, внесённые
|
||||
правками самого акта. Строки **PD-315…PD-327**, каждая с механизмом и пином. Два из них —
|
||||
собственные пины акта, проходившие под мутацией, которую сами называют (PD-318): это третье
|
||||
появление класса в паке, и он назван в плане §3 п.2 ещё до того, как повторился.
|
||||
|
||||
- **P7, акт 5 (начало) — исполнение фиксов по ревью акта 4 + ответ контрактной сессии.**
|
||||
План и рабочий список — **`platform/docs/archive/P7_ACT5_FIX_PLAN_2026-08-20.md`** (читать первым; §5а — что пришло из
|
||||
контракта).
|
||||
⚠ **Контрактная сессия ответила 20.08 на записку зоны `archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md`: канон стал
|
||||
0.4.0** (лежит в дереве контрактной зоны, НЕ ратифицирован — до лендинга в силе 0.3.0). Все три
|
||||
вопроса, висевшие на владельце, закрыты. Зоне пришла работа: `resume` исчерпанного прогона — 409 с
|
||||
двумя РАЗНЫМИ причинами (`ceiling_reached` против нового `credit_unavailable`); «глава сделана» =
|
||||
последний реально пройденный проход, и это ДЕНЬГИ — `ChaptersLeft` на деплое без волны редактора не
|
||||
подрезается вовсе, сервис бесконечно предлагает купить уже переведённое; отпечаток интейка — по
|
||||
содержимому, `Content-Length` из нормы вышел, а невозможность установить тождество обязана давать
|
||||
409, а не реплей; `Run.stop_requested` обязательным булевым (единственная ломающая правка).
|
||||
Отклонены: слово под суточный потолок движка (решение владельца 15.08) и предложенный зоной признак
|
||||
«прогон продолжаем» (три лишних чтения на карточке, которую перечитывают на каждый кадр).
|
||||
Акт 4 закрылся адверсариальным ревью СОБСТВЕННОГО диффа (9 линз × 2 рефутера, 75 агентов):
|
||||
**33 находки, 27 пережили рефутеров**, и часть из них внесли сами правки акта 4. Тяжелейшая —
|
||||
четыре независимые линзы сошлись на том, что **реконсилятор снимал стоп подписи банка, которого
|
||||
никто не подписывал**, и доспавнивал движок без `--verify-bank`: складывая снятие стопа внутрь
|
||||
общего `reopen`, акт 4 не проверил, что `reopen` зовёт не только `resume`, но и свип. Нарушение
|
||||
согласия пользователя; починено ключом на вызывающего, пин проверен посадкой мутации.
|
||||
⚠ **Первый написанный на это пин ПРОШЁЛ под мутацией** — фикстура не доходила до кода, потому что
|
||||
часы сервиса захвачены при конструировании и `f.now` их не двигает. Урок в плане, §3.
|
||||
Закрыто также: загрузочная проверка пар — на ДОСТУПНОЙ половине (деплой с одними `:unavailable`
|
||||
больше не стартует), интейк не трактует пустой список как «пускать всё», теги сравниваются
|
||||
регистронезависимо. Батарея: 18 пакетов, exit 0, скипов 0, тестов **503**.
|
||||
|
||||
- **P7, акт 4 — доработка 20.08: сверка ВСЕХ находок против дерева + триаж §8.2.**
|
||||
(Пакет релеев `archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md` §7; три вопроса владельцу §8.1а; вопросы контракта
|
||||
вынесены в `platform/docs/archive/CONTRACT_SYNC_FROM_PLATFORM_2026-08-20.md` для отдельной контрактной сессии.)
|
||||
Все 74 позиции обеих приёмок сверены поштучно с ТЕКУЩИМ деревом — каждая с цитатой живого кода,
|
||||
исход «не проверено» отдельно от «опровергнуто».
|
||||
⚠ **Три места числились закрытыми и закрыты не были — правка не закрывала собственный сценарий:**
|
||||
(1) подрезка буфера потока сверялась ПОСЛЕ того, как водяной знак перепрыгнул дыру, поэтому
|
||||
условие не срабатывало никогда и кадры `note` терялись молча (PD-283); (2) `CompleteIdempotency`
|
||||
писала квитанцию в строку, которая попытке уже не принадлежит — воспроизведено живым PG, клиент
|
||||
получал квитанцию ЧУЖОГО запроса (PD-284); (3) пин на снятие `--verify-bank` был ПУСТЫМ: фикстура
|
||||
стартовала прогон без `stop_for_signing` (PD-285). Все три «подтверждались» зелёной батареей.
|
||||
**Ещё закрыто:** HEAD на потоке больше не держит горутину и ошибка `Flush` не выдаётся за
|
||||
отправленный кадр (PD-286) · `blocked` называет чужую книгу только когда её холд укорачивает шкалу
|
||||
(PD-287, пина не было вовсе) · `note_count` и `GET /notes` описывают одно множество (PD-288) ·
|
||||
устаревшая доставка не рождает кадр (PD-289) · чтения на одном снапшоте (PD-290) · флаг «текст
|
||||
прочитан» переехал на ПАРУ (PD-291) · снятие стопа банка внутри транзакции переоткрытия, отдельного
|
||||
писателя больше нет (PD-292) · проход материализации подрезается своим бюджетом (PD-293) ·
|
||||
блокировка книги в решениях банка (PD-294) · дубликат индекса снят (PD-295) · маршруты и коды
|
||||
ошибок — по ОДНОЙ таблице, тесты ходят по ней (PD-296) · бэкстоп-свип для книги без дерева
|
||||
(PD-276 дозакрыт) · интейк не платит за третью ре-нарезку (PD-248 актуализирован).
|
||||
**Триаж §8.2:** 17 сделано, 5 отклонено с причиной, 1 релей. Батарея: 18 пакетов, exit 0, скипов 0,
|
||||
линтер 0 issues, тестов **458 → 501**; каждая правка проверена посадкой мутации, не рассуждением.
|
||||
⚠ **Осознанно НЕ сделано, с причиной:** PD-297 (round-trip на строку под блокировкой книги —
|
||||
мерить прежде правки) · PD-202 (draft-only, вопрос владельцу) · PD-282 (канон §resumeRun не
|
||||
покрывает «денег не осталось») · PD-298 (сперва сверка у движка) · PD-299.
|
||||
**Осей, которых не смотрел никто:** деньги и леджер · вход/сессии/CSRF · очередь и джобы · метрики.
|
||||
|
||||
- **P7: приёмка → правки → приёмка правок (17.08).**
|
||||
⚠ Полный хендофф под сессию без контекста — **`platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md`** (читать §0–§3,
|
||||
дальше по нужде). Там же: принципы приёмки, сверка с исходным промтом, техдолг, план следующей сессии.
|
||||
Категорийное ревью по 12 осям с адверсариальной верификацией: 80 находок, **54 подтверждено, 16
|
||||
правдоподобно, 10 опровергнуто**; после дедупа **46 уникальных дефектов** — 3 блокирующих класса,
|
||||
15 HIGH, 17 MEDIUM. Батарея была зелёная, потому что все три блокирующих дефекта живут в путях,
|
||||
которые тесты проходили только в безопасной форме.
|
||||
**Починено в зоне** (регистр PD-257…PD-281): пере-нарезка книги больше не валит читающую модель
|
||||
навсегда (отложенный констрейнт, миграция 00018) · упавший `tmctl export` больше не затирает текст
|
||||
пустотой · весь блок идемпотентности (гонка первых попыток 500→409, область ключа, отпечаток с
|
||||
размером файла, закрытие ключа вне контекста запроса) · база полосы прогресса считает тот же
|
||||
проход, что числитель · SSE (подрезанные кадры → `resync_required`, дедлайн записи, id из
|
||||
будущего) · материализация не голодает свип · банк читается на штатном деплое · деплой-нота больше
|
||||
не включает обратно грант, который PD-104 выключил. Батарея: 18 пакетов, exit 0, скипов 0,
|
||||
линтер 0 issues, тестов **458 → 490**.
|
||||
⚠ **Приёмка ПРАВОК (9 осей + fable-5) нашла, что восемь дефектов внесли сами правки** — батарея не
|
||||
увидела ни одного, три были запинены под ошибку автора. Все исправлены; разбор — хендофф §0.
|
||||
**Б-1 ПОЧИНЕН разбором с владельцем 17.08 (PD-277).** Рамка приёмки была неверна: это не «нужен
|
||||
новый канал в движок», а невыполненная половина строки единого бэклога **191(в)** (слово владельца
|
||||
16.08, D39.144) — «проводка `resume = снятие стопа` до движка». Движок при выключенном
|
||||
`--verify-bank` уже делает модель владельца: неподписанное едет авто-строками с пометкой
|
||||
⟨проверить⟩ (`mining.go:211-231`, D39.42 п.3), а платформа передавала флаг обратно и роняла прогон
|
||||
в тот же стоп. Снято: `l.VerifyBank && !l.BankReleased`; пин `TestAResumedRunIsSpawnedWithoutTheSigningStop`.
|
||||
⚠ Остаток: `decline` пользователя до движка не доезжает (движок читает `mined_rejects`) — строка
|
||||
бэклога **192**, отложена владельцем.
|
||||
**PD-273 закрыт решением владельца 17.08:** статус остаётся `awaiting_bank`, лечение — признак
|
||||
«стоп был запрошен» в контракте; готовая строка для единого бэклога — хендофф §7(и), вставляет
|
||||
оркестратор (зона `docs/` не наша).
|
||||
**Разбор, план, обязательства через оркестратора и команды воспроизведения —
|
||||
`platform/docs/archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md`.**
|
||||
⚠ Заявки раздела «Сессия P7» ниже приёмку НЕ прошли — читать их вместе с хендоффом, не вместо.
|
||||
|
||||
- **P7 ПОСТРОЕН — заявка САМОЙ сессии написания (17.08).** ⚠ **Приёмку эта заявка НЕ прошла** — см.
|
||||
верхнюю запись и `archive/P7_ACCEPTANCE_HANDOFF_2026-08-17.md`: 46 уникальных дефектов, три блокирующих класса, из них
|
||||
ни один батарея не видела. Читать ниже как исходную позицию, а не как состояние дерева.
|
||||
Читающая поверхность контракта **0.3.0**:
|
||||
главы · пары с текстом · замечания · банк и подпись · SSE на КНИГЕ · `GET /capabilities` ·
|
||||
машинная модель ошибок (`code` + `request_id`) · условные чтения и сжатие · `Idempotency-Key` ·
|
||||
`structure_version`. Плюс форварды (`title`, `reject_reason`), PD-104 (грант → 0), демонтаж
|
||||
построенного гейта полноты банка (D39.144), PD-241, конформность построенного по §5 промта и
|
||||
рантбук деплоя, прогнанный ЖИВЫМ `migrate`. Батарея на стенде `~/.local/pgsql` (порт 55433) под
|
||||
`-race` с ОБОИМИ гейтами: **EXIT=0, 18 пакетов, скипов 0, линтер 0 issues**, тестовых функций
|
||||
**416 → 458**. Дифф: 46 файлов, +2329/−646. Селф-ревью финалом — ТРИ независимых ревьюера, два из
|
||||
них другой модели: **22 находки, 17 подтверждены и починены** (две HIGH нашли оба контрактных
|
||||
ревьюера независимо), 5 — диспозиция с аргументом. Разделы ниже — записка-план, живые пробы,
|
||||
таблица селф-ревью, obstacle, вопросы оркестратору.
|
||||
- **P8-FIX ПРИНЯТ И ЗАЛЕНДЕН оркестратором №18 (22.08).** Тело приёмки — ратифицированная нота
|
||||
**D39.154** (`docs/architecture/05-decisions-log.md`), здесь не пересказывается. Зоне важно ровно
|
||||
следующее, и этого в ноте нет:
|
||||
- **Пинг зоны №1 ЗАКРЫТ:** якорь `05-decisions-log.md:542` → `internal/pgstore/perf_test.go:13` жив,
|
||||
токен «96% of the page» на месте — правку комментария бенчмарка приёмка приняла.
|
||||
- **Правку зоной ЧУЖОГО документа** (пере-нацеливание двух моих якорей в промте читающего пака
|
||||
после переезда констант) **утверждаю и не откатываю**: якоря умерли по вине пака, пак их починил
|
||||
и раскрыл это сам, вместо того чтобы обойти красный гейт. Это ровно то поведение, которого норма
|
||||
и требует, — так и делайте дальше.
|
||||
- **«Скипов 0» у меня НЕ воспроизвелось: три скипа.** Не ваша регрессия и не ошибка отчёта —
|
||||
`systemdOrSkip` гейтит три теста `internal/runner` достижимостью пользовательского менеджера
|
||||
systemd, а на моём хосте `/run/user/1000` нет. Незакрытым осталось то, что рецепт и критерий
|
||||
приёмки объявляли у батареи ДВА гейта, а их три: `PD-374`, оба носителя поправлены 22.08.
|
||||
- **Мои одиннадцать посадок мутаций вне вашего списка:** убито восемь, выжили три — `StalledAfter`
|
||||
законно (носитель один), `maxAttempts` и `truncateReason` дали `PD-373`/`PD-372`. Плюс находка
|
||||
вне карты пака `PD-371`. ⚠ Две первых редакции МОИХ посадок дали ложное «выжила» — гонял не тот
|
||||
пакет; пере-прогнано. Говорю, потому что необъявленная ошибка харнесса приёмки — находка, которой нет.
|
||||
- **Числа сдачи в вашем отчёте держатся**, кроме одного: «отпечатков миграций 41» — их 25
|
||||
(`grep -c '^[0-9a-f]\{64\}' migrations.sha256`); число было названо без команды. Регистр после
|
||||
приёмки — **374 строки, открытых 72** (1 major, 16 minor, 55 info).
|
||||
- **Открытым уезжает `PD-370`** (контрактная половина) — не работа зоны, закрывать её здесь было бы
|
||||
подгонкой под критерий приёмки. Согласен с вашей диспозицией.
|
||||
⚠ **Порядок паков вышел ОБРАТНЫМ очереди:** промт P8-FIX требовал запуска ПОСЛЕ читающего пака,
|
||||
запущен был раньше. В плюс — названная цена перестановки («блокер живёт всё время читающего пака»)
|
||||
НЕ заплачена. В минус — §4.7 релея пуст, и читающий пак пойдёт по только что переписанному коду;
|
||||
его промт про это предупреждён баннером.
|
||||
|
||||
- **Эра пака P8-FIX (21–22.08) — В АРХИВЕ.** Отчёт пака, обе волны ревью, спил пер-термной подписи, инвентарь каналов и obstacle — [archive/platform-PROGRESS-P8.md](archive/platform-PROGRESS-P8.md). Ратификация — D39.154, лендинг `31f1f82`. Живое из этой эры: четыре открытые строки регистра (`PD-370` мажор — контрактная половина · `PD-371` · `PD-372` · `PD-373`/`PD-374`), инвентарь каналов шва в `STACK_DECISIONS.md`, граница sqlc в `BACKLOG.md` П-19.
|
||||
|
||||
- **Записи акта 5 пака P7, остававшиеся в живом журнале, — ДОСЛАНЫ в [archive/platform-PROGRESS-P7.md](archive/platform-PROGRESS-P7.md)** 22.08: заголовок «эра P7 в архиве» стоял, а тела лежали здесь.
|
||||
|
||||
- **Запись оркестратора, 15.08 — P6 + ДОФИКС ПРИНЯТЫ И ЗАЛЕНДЕНЫ (D39.132).** Приёмка двумя
|
||||
раундами: панель 5 адверсариальных верификаторов по P6 (две линзы — другой моделью; исполнением,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue