textmachine/platform/docs/platform-PROGRESS.md

265 lines
63 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# Журнал зоны «Платформа»
> Весь прогресс платформы — ЗДЕСЬ (решение владельца 04.08): пинги, итоги сессий, открытые
> вопросы, предложения на ратификацию. В `docs/PROGRESS.md` платформа не пишет; оркестратор
> читает этот журнал при каждом лендинге зоны (свип «решений владельца» — норма D39.99 п.4).
## Текущее состояние
**⛔ ПИНГ ОРКЕСТРАТОРА №19 — 23.08, ЗОНЕ ПЛАТФОРМЫ. Три находки, все сверены моими командами; рукой в код не лезу.**
1. **Пере-нарезка книги сносит ВСЕ решения юнитов и пересчитывает прогресс из пустоты.**
`internal/pgstore/readmodel.go:127-137`=`a re-cut drops the resolutions of the previous cut`
это не догадка, там собственный комментарий кода. Строку в ваш регистр я не заводил: решать вам,
намеренная это семантика пере-разреза или дефект, и что делать со счётчиками, которые после
этого показывают ноль сделанного на книге, где работа была.
2. **⛔ БЛОКЕР ЖИВОГО ПРОГОНА: движок на SaaS не получает провайдерских ключей** — строка **211**
единого бэклога, тело там. Ваша половина: путь к файлу ключей едет из конфига платформы в
аргументы движка, а дев-супервизор переводится на тот же механизм. Сейчас пути РАЗНЫЕ —
`internal/ingest/supervisor.go:35-36,65-67`=`Provider keys reach the engine through it` наследует
окружение платформы, а прод-спавн ставит юниту ровно один элемент
(`internal/runs/spawn.go:168`=`Env: engineEnv(engineStreamID(`). Поэтому стенд зелёный, а прод
голодает, и ни один тест упасть не мог. Пока два пути кормят движок по-разному, следующий такой
блокер снова пройдёт всю батарею.
3. **Дубль движковой конвенции у вас.** `internal/runner/artifacts.go:65-95`=`neither project_db nor book_id`
сам вычисляет путь БД книги, повторяя `backend/internal/config/book.go:163-167`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")`;
смена дефолта в движке тихо уведёт ваше чтение банка на несуществующий путь. Строка **213**.
Фикс аддитивный и без ломки: движок отдаёт путь артефакта, вы выкидываете свой `projectDB()`.
⚠ Пере-именование банк-экспорта в фикс-имя — ЛОМАЮЩЕЕ, ему место в окне строки 161, не здесь.
*(Промт `P8-REVIEW` жив и ждёт слова владельца; два дефекта в нём я нашёл и починю ДО выдачи —
одна из названных «денежных» миграций денежной не является, и счёт чужого дерева в шапке протух.)*
- **P8-FIX ПРИНЯТ И ЗАЛЕНДЕН оркестратором №18 (22.08).** Тело приёмки — ратифицированная нота
**D39.154** (`docs/architecture/05-decisions-log.md`), здесь не пересказывается. Зоне важно ровно
следующее, и этого в ноте нет:
- **Пинг зоны №1 ЗАКРЫТ:** якорь `05-decisions-log.md:542``internal/pgstore/perf_test.go:13` жив,
токен «96% of the page» на месте — правку комментария бенчмарка приёмка приняла.
- **Правку зоной ЧУЖОГО документа** (пере-нацеливание двух моих якорей в промте читающего пака
после переезда констант) **утверждаю и не откатываю**: якоря умерли по вине пака, пак их починил
и раскрыл это сам, вместо того чтобы обойти красный гейт. Это ровно то поведение, которого норма
и требует, — так и делайте дальше.
- **«Скипов 0» у меня НЕ воспроизвелось: три скипа.** Не ваша регрессия и не ошибка отчёта —
`systemdOrSkip` гейтит три теста `internal/runner` достижимостью пользовательского менеджера
systemd, а на моём хосте `/run/user/1000` нет. Незакрытым осталось то, что рецепт и критерий
приёмки объявляли у батареи ДВА гейта, а их три: `PD-374`, оба носителя поправлены 22.08.
- **Мои одиннадцать посадок мутаций вне вашего списка:** убито восемь, выжили три — `StalledAfter`
законно (носитель один), `maxAttempts` и `truncateReason` дали `PD-373`/`PD-372`. Плюс находка
вне карты пака `PD-371`. ⚠ Две первых редакции МОИХ посадок дали ложное «выжила» — гонял не тот
пакет; пере-прогнано. Говорю, потому что необъявленная ошибка харнесса приёмки — находка, которой нет.
- **Числа сдачи в вашем отчёте держатся**, кроме одного: «отпечатков миграций 41» — их 25
(`grep -c '^[0-9a-f]\{64\}' migrations.sha256`); число было названо без команды. Регистр после
приёмки — **374 строки, открытых 72** (1 major, 16 minor, 55 info).
- **Открытым уезжает `PD-370`** (контрактная половина) — не работа зоны, закрывать её здесь было бы
подгонкой под критерий приёмки. Согласен с вашей диспозицией.
**Порядок паков вышел ОБРАТНЫМ очереди:** промт P8-FIX требовал запуска ПОСЛЕ читающего пака,
запущен был раньше. В плюс — названная цена перестановки («блокер живёт всё время читающего пака»)
НЕ заплачена. В минус — §4.7 релея пуст, и читающий пак пойдёт по только что переписанному коду;
его промт про это предупреждён баннером.
- **Эра пака P8-FIX (2122.08) — В АРХИВЕ.** Отчёт пака, обе волны ревью, спил пер-термной подписи, инвентарь каналов и obstacle — [archive/platform-PROGRESS-P8.md](archive/platform-PROGRESS-P8.md). Ратификация — D39.154, лендинг `31f1f82`. Живое из этой эры: четыре открытые строки регистра (`PD-370` мажор — контрактная половина · `PD-371` · `PD-372` · `PD-373`/`PD-374`), инвентарь каналов шва в `STACK_DECISIONS.md`, граница sqlc в `BACKLOG.md` П-19.
- **Записи акта 5 пака P7, остававшиеся в живом журнале, — ДОСЛАНЫ в [archive/platform-PROGRESS-P7.md](archive/platform-PROGRESS-P7.md)** 22.08: заголовок «эра P7 в архиве» стоял, а тела лежали здесь.
- **Запись оркестратора, 15.08 — P6 + ДОФИКС ПРИНЯТЫ И ЗАЛЕНДЕНЫ (D39.132).** Приёмка двумя
раундами: панель 5 адверсариальных верификаторов по P6 (две линзы — другой моделью; исполнением,
включая живой демон и живой PG) → фикс-лист ФП-1…ФП-8 → дофикс → финальная верификация
исполнением: батарея `make check` под `-race` с живым PG и ОБОИМИ гейтами пере-прогнана
оркестратором — EXIT=0, 17 пакетов, скипов 0; тестов **416** (`grep -rh '^func Test' platform
--include='*_test.go' | wc -l`; HEAD = 359); деньги пере-считаны из сырого леджера стендовой БД
двумя путями (сошлись до цента); живая проба рендера пере-снята своим tmctl из HEAD; 2
собственные посадки приёмки на дофикс — обе пойманы (`TestAFlaggedBookStillAnswersAboutItsMoney`
· `TestEachOfTheThreeBlockersAloneKeepsABookOutOfTheMigrationList` — вторая до дофикса переживала
всю батарею). **Исправлено оркестратором при лендинге:** откачено переименование закрытой строки
PD-198 → PD-199 и удалена заведённая под PD-198 строка-дубль с ложным обоснованием (регистр
246 → 245 слиянием; «ID стабилен навсегда»); PD-200 перенесена в «Закрытые — эра P6», PD-180 —
в «Открытые — info». Ратификации и диспозиция вопроса PD-241 — тело D39.132.
- **ДОФИКС P6 ЗАКРЫТ — дерево передаётся на лендинг одним пакетом с P6 (15.08).** Фикс-лист приёмки
ФП-1…ФП-8, раздел «Дофикс P6» ниже. Ни одна находка не опровергнута, но две подтвердились не так,
как их описала приёмка. Самая тяжёлая — денежная и на ОБЫЧНОМ пути: движок отвечает exit 2 из
`status --json` про любую книгу с помеченной единицей, а платформа читала это как «движок не
ответил», из-за чего расчёт такого прогона откладывался вечно и следующий прогон книги не
стартовал. Плюс аддендум владельца 15.08: `day_usd` убран из ПРИМЕРА шаблона книги. Батарея под
`-race` с обоими гейтами: EXIT=0, 17 пакетов, скипов 0, линтер 0 issues, `make vuln` чист,
**359 (HEAD) → 416** тестовых функций — счёт командами в разделе. Посадки 15 из 15.
- **P6 ПОСТРОЕН — дерево передаётся на лендинг (14.08).** Потребительская половина шва эмиттера
(П-15) · интейк формы Б (П-14) · дев-стенд с сидом и дев-входом (П-16) · порядок апгрейда движка
против деадлока v15 · попутные PD · гигиена зонных доков. **PD-113 закрыт — открытых major в
регистре НЕТ.** Батарея на стенде `~/.local/pgsql` (порт 55433) под `-race`: EXIT=0, 17 пакетов,
линтер 0 issues, скипов 0, `make vuln` чист, тестов зоны **359 → 401** (числа P6; счёт — команды
в разделе «Дофикс P6», прежний базис «396 → 403» не воспроизводился, PD-234). Посадки:
20 из 20 в первом круге (три переписаны после разбора переживших) + 6 из 6 на находки
адверсариальной панели, одна названа НЕПИНЯЕМОЙ по построению. Живые пробы на настоящем `tmctl`
и на фейке движка с правилом потолка — разделы ниже.
**Три вопроса на ратификацию оркестратору** (подробности в разделе P6): внутренняя причина
`daily_ceiling` НЕ проецируется на провод (контракт знает одно значение — PD-199, спек-правка
за S4) · направление `PLATFORM_DIRECTION` §3 предложено ПЕРЕ-ПОДПИСАТЬ (oapi-codegen и sqlc не
исполнены, срок «до первого хендлера» пройден) · батарея получила ВТОРОЙ гейт окружения
(`TM_PLATFORM_TEST_ENGINE_BIN` + `_BOOK_TEMPLATE`), без него один тест честно скипается.
- **P5 ПОСТРОЕН и ДОФИКШЕН по приёмке — дерево передаётся на лендинг (11.08).** *(→ принят в три раунда и заленден D39.130, `69d485a` — запись оркестратора 14.08 ниже.)* `POST /v0/books`
потоково с пер-маршрутным потолком тела и своим дедлайном чтения · статусы
`uploading → parsing → not_started | rejected` получили писателей, разбор — $0-команда движка
`tmctl manifest` · `POST /v0/runs/{id}/stop|resume` с намерением стопа в Postgres ДО сигнала ·
метрики Prometheus на отдельном слушателе · печать эффективной конфигурации.
- **Приёмка 11.08 (8 находок) → дофикс → кросс-семейное ревью дофикса (5 находок) → ре-чек
оркестратора (3 находки, 2 high) → ТРЕТИЙ РАУНД ЗАКРЫТ (14.08)**: разделы «Третий раунд»,
«Дофикс-2» и «Дофикс P5» ниже, движение — записками-планами. Батарея на стенде `~/.local/pgsql`
(порт 55433) под `-race`: EXIT=0, 16 пакетов, линтер 0 issues, скипов 0, `make vuln` чист, тестов
**261 → 359**. Посадки: 10/10 · 6/6 · 6/6 (две пережившие за все раунды — оба раза виноват был
слепой пин, переписан пин, не мутация); списки — в журнале.
**Тулчейн 1.26.6 НА РАТИФИКАЦИЮ** *(→ ратифицирован D39.130)*, и вчерашний клейм про него был ЛОЖЕН: гейт принимал 1.26.5
(регекс вместо сравнения). Теперь floor держат два механизма — `make version-check` сравнивает
версии, `go.mod` несёт `toolchain go1.26.6`, который читает всякая сборка; при `GOTOOLCHAIN=auto`
хост скачает нужный тулчейн, при `=local` остановится. Само сравнение запинено (`internal/gates`).
**ВОПРОС на ратификацию — кто пишет `book.yaml` при интейке**: половина П-9 стоит на нём, раздел
«Развилка» ниже. *(→ решено D39.130: бета = форма Б, стройка = П-14; движковая форма В = строка 170 единого.)*
- **Запись оркестратора, 14.08 — P5 ПРИНЯТ И ЗАЛЕНДЕН (`69d485a`, D39.130).** Финальная
верификация исполнением: батарея EXIT=0 на том же стенде · **гейт версий живьём отказывает
1.26.5 и `go1.27rc1`, принимает 1.26.6 и 1.26.10** (sort -V честный) · `toolchain go1.26.6`
в go.mod · сентинел `.tmplatform-books` на месте · `revision+1` остался ровно у двух заявленных
прогресс-писателей синка (мотивированное исключение ратифицировано) · тестов **359**, регистр
**198**, удалённых имён тестов против HEAD нет. **Ратифицировано (D39.130):** Go-floor 1.26.6
с toolchain-директивой (цена офлайн-хоста с `=local` названа и принята) · `book.yaml` при
интейке — форма **Б** (рендер из деплой-шаблона; интерпретация D39.110 §2b с аудит-следом,
стройка — следующим касанием зоны), форма **В** (`tmctl init`) — строка 170 единого бэклога
движку · контракт-диспозиции PD-172/173/174/180 — спек-правкой 0.2.3 задачей S4-промта ·
PD-196 → дописка строки 165 (различимость классов отказа `manifest`).
- **Пинг оркестратора, 14.08 (второй) — ЭМИТТЕР ДВИЖКА ЗАЛЕНДЕН (D39.131, `9cfe080`): `events.jsonl` начнёт появляться в каталогах книг.** Словарь = ваш `events.go` с диффом движка (принят приёмкой): `StreamVersion` **1.1** · `Ceiling.Scope: book|day` · `Finished.outcome += ceiling|stopped` · `eta_seconds` = темп ТЕКУЩЕГО прогона · `unit_done.unit` = лидерный `first_chunk_idx`. Ваша половина — **П-15** зонного бэклога (маппинг exit 4/5/1019 · `unit_done` присваиванием · scope · dev-супервизор · порядок деплоя против деадлока v15 — строка 174 единого, РЕШИТЬ ДО деплоя нового бинаря). PD-113/PD-196 в регистре остаются open до П-15 — движковая половина построена, потребительская нет.
- **Пинг оркестратора, 14.08 — гигиена зонных доков по аудит-свипу корпуса, закрыть следующим
касанием зоны (вместе с П-14):** `README.md:85` «тулчейн ≥1.26.5» → 1.26.6 c toolchain-директивой ·
`README.md:87` «River не подключена — П-3» и `STACK_DECISIONS.md:23` «River в go.mod НЕ добавлен» —
противоречат go.mod и построенной P4-очереди · `README.md:47-48` вопрос book.yaml → решён D39.130
(форма Б) · `PLATFORM_DIRECTION.md`: oapi-codegen «взять — доказано» против `ENGINEERING_STANDARDS:59`
«кандидат при P1» — оба не исполнены (ручки P4/P5 рукописные), sqlc «до первого хендлера» не
случился (PD-44 открыт), «River в go.mod не заводить» отстал — направление пере-подписать честно ·
`DEFECT_REGISTER.md`: PD-172/173/174/180 не несут диспозицию D39.130 (спек-правка 0.2.3 задачей S4) ·
открытые PD-180/185/196 живут в секциях «Закрытые…» · PD-178 стоит после PD-191 (порядок номеров) ·
PD-99 (fixed кодом) лежит в «Закрытых ратификацией» · путь `docs/scripts/counts.py` в шапке
регистра исполним только от корня репо · веса `high`/`medium-high` секций дофиксов против
объявленных major/minor/info · `STACK_DECISIONS.md:17` называет гейт «make tools-check», хотя
`version-check` отделён (FP5-9) · П-1/П-3 зонного бэклога не знают построенного P4 (вес «Ф3, после
контракта» отстал) · хвост (г) третьего раунда — перечислительность мета-пина systemd-гейта —
носителя не имеет: завести PD-строку или закрыть явно.
- **P4 «раннер» + дофикс + V2 ПРИНЯТЫ и ЗАЛЕНДЕНЫ — D39.123, код `d29e30c`** (ревью-шапка ниже):
транзиентный systemd-юнит на прогон · очередь River с холдом-до-спавна в одной транзакции ·
реконсилятор с базовой линией расчёта · тейлер `events.jsonl` с карантином проекции · пять
ручек `/v0` контракта **0.2.1** · дев-интейк. Батарея с живым PG 18.4 под `-race` зелёная,
скипов 0, тестов зоны **261**. Формула аргумента потолка — `committed + прирост` (PD-158,
ратифицирована ПОПРАВКОЙ к пингу оркестратора).
- **Регистр — 245 строк** (`python3 docs/scripts/counts.py` от корня репо; было 246 — лендинг слил
строку-дубль PD-198, испр. оркестратором №16 15.08): 178 закрыто · 1 закрыт ратификацией (PD-59)
· 4 риском · **62 открыто (17 minor, 45 info), из них 0 major** — PD-113 закрыт P6 вместе с
приходом эмиттера. Форма — 15 секций (добавлены эра P6 и дофикс P6).
- **Промт P5 ВЫДАН оркестратором 10.08** (`PLATFORM_P5_SESSION_PROMPT.md`, D39.128): П-9
`POST /books` (+PD-72 потолок тела; развилка «кто пишет book.yaml при интейке» — вопросом в этот
журнал ДО стройки той половины) · стоп/резюм-ручки PD-140 (различение «потолок vs авария» —
за эмиттером движка, строки 103/165, его промт выдан параллельно) · наблюдаемость П-11 +
печать конфигурации PD-114 · DEFECT_REGISTER секциями (пинг №16). Запуск — по слову владельца.
*(→ исполнен сессией P5 1114.08, принят D39.130; промт с баннером-исходом — `archive/PLATFORM_P5_SESSION_PROMPT_2026-08-10.md`.)*
Эскроу/uncertain (строка 136) — следующим денежным промтом, НЕ в P5.
- Эры P0P3 (вход OIDC · кредиты · админ-CLI · деплой · фикс-паки) — исполнены и залендены
(D39.107/109/112/114); разделы — в [archive/platform-PROGRESS-P0-P3.md](archive/platform-PROGRESS-P0-P3.md).
Стек и рецепт стенда — `STACK_DECISIONS.md`; критерии приёмки — `ENGINEERING_STANDARDS.md`.
*(Записи паков P4P6 с дофиксами (0815.08) — в [archive/platform-PROGRESS-P4-P6.md](archive/platform-PROGRESS-P4-P6.md); вынесено 16.08 по слову владельца.)*
## Эра пака P7 (1620.08) — в архиве
Пять актов, приёмка, фикс-раунды и таблицы селф-ревью выселены срезом в [`archive/platform-PROGRESS-P7.md`](archive/platform-PROGRESS-P7.md) (норма: закрытая эра не живёт в журнале). Итог и то, что пережило пак, — шапка выше и пинг оркестратора №18 ниже.
## Закрытые эры P0P3 — в архиве
Разделы сессий P0P3 и их ратификаций (0408.08) вынесены в
[archive/platform-PROGRESS-P0-P3.md](archive/platform-PROGRESS-P0-P3.md) (D39.124).
Решения оттуда живут в D-логе и `DEFECT_REGISTER.md`.
## Пинги оркестратора (живые ссылки для следующих сессий)
**Пинг оркестратора №15 (09.08, D39.122): движковый пак «блокеры контракта» ПРИНЯТ и заленден `0e69bc1` — пять поверхностей для платформы существуют. ФИНАЛЬНЫЕ формы (менялись трижды за приёмку — старые в переписке игнорировать):**
- **Манифест глав:** `<project_db>.manifest.json`, `manifest_version: "tm-manifest-v2"` (v1 движок сам отклоняет); id главы = 16 hex (стабилен через пере-нарезку); **`unit.id = <chapterID>:<cutTag>:<firstChunkIdx>`** — тег разреза 8 hex, при любой смене нарезки/данных пары unit.id УМИРАЮТ намеренно (id жив ⇒ якорь цел); поле `heading` — ВРЕМЕННЫЙ рендер движка «Глава N», НЕ метка книги (решение владельца 09.08; настоящие заголовки — строка 160 бэклога движка). $0-команда `tmctl manifest` строит дерево до первого прогона (первое касание создаёт БД проекта — то же поведение, что у status).
- **Прогресс:** `status --json``progress: {draft:{done,total}, edit:{done,total}}` на книге и в каждом элементе `chapters`; `done` = «разрешено волной» (ok/flagged/skipped); волна, которой нет, — `total: 0`; процент не отгружается — собирает клиент.
- **Банк:** `<project_db>.bank.json` (весь банк тремя статусами; id термов — длино-префиксированный хеш ключа уникальности, стабилен через пересборку) и `<project_db>.bank-stop.json` (полная таблица подписи; `conf: null` ≠ 0). ⚠ **ИСПРАВЛЕНО оркестратором №18 (20.08, линза шва P7): «ВСЕ сайдкары атомарно» — НЕВЕРНО.** Атомарны (temp+`Sync`+rename, `backend/internal/pipeline/artifact.go:24-59`) ровно три — `manifest.json`, `bank.json`, `bank-stop.json`, — и читать во время прогона можно ИХ. `bank-stop.txt`, `mined-signature.yaml`, `auto-bank.yaml` пишутся `os.WriteFile` с усечением первым делом: читателей у них сегодня нет, и сессия, взявшая любой по прежней формулировке, получила бы усечённый документ на живом прогоне.
- **Потолок (строка 145): `tmctl translate|redrive --ceiling-usd <usd>` — это КНИЖНЫЙ ПОТОЛОК В СИЛЕ, не бюджет прогона** (ратифицировано D39.122): леджер сравнивает значение с накопленным committed+reserved книги. Пересчёт пользовательского «прирост в главах» (D39.110) в абсолют — ОБЯЗАННОСТЬ платформы. **⚠ АМЕНДИРОВАНО D39.123 (PD-158): формула = `committed_usd + прирост×оценка`, БЕЗ reserved** — read-only `status` показывает leftover-reserved, который `store.Open` зануляет до первой судимой резервации; включение переплачивало бы запасом сверх холда (исполнено обеими формулами против гейта). `reserved_usd` читается обязательным полем как гард присутствия и улика leftover. Значение ниже уже потраченного откажет первой же резервации. День-потолок не перекрывается. Опция по желанию зоны: движок готов провести `--ceiling-usd` и в `status`, чтобы мониторинг видел действующий потолок capped-прогона (сегодня status показывает книжный) — скажите, заведём строку.
## Пинг оркестратора №16 — 09.08.2026 (форма DEFECT_REGISTER, строка 167)
Реестр дефектов дорос до 171 строки одной таблицей; докс-аудит D39.125 (worksheet `docs/archive/reports/DOC_AUDIT_INVENTORY_2026-08-09.md`, жалоба «зонная изоляция прячет половины тем») просит форму: разложить таблицу на СЕКЦИИ (open по весу · accepted-risk · fixed по эрам паков), сохранив построчную форму `| PD-N | … |``docs/scripts/counts.py` ключуется формой строки, не позицией, секции его не ломают. Заведите П-строкой в свой BACKLOG, исполнение — попутно следующим паком, НЕ срочно. Вопросы — через владельца.
## Пинг оркестратора №17 — 15.08.2026 (migrate принят, D39.134 — движковая половина самолечения ФИНАЛЬНА)
Бэкенд-пак `tmctl migrate` принят и заленден (`d55edd4`): exit 13 = `schema_mismatch` финализирован, токен `schema_mismatch found=N expected=M` на stderr стабилен, write-путь тоже отказывает БД новее бинаря. Ваш PD-201 (самолечение «поймал 13 → migrate → повтор») теперь можно строить — движок больше не сдвинется под ногами; строку реестра, ссылающуюся на «движкового migrate ещё нет», обновите. Хвосты вашей зоны, найденные приёмкой (вход промта P7, исполнение попутно): (1) `deploy/README.md:131` цитирует МЁРТВЫЙ текст ошибки схемы («schema vN … expects vM») — движок его больше не печатает; (2) та же мёртвая цитата и уплывший якорь `store.go:135-143` в строке П-1 вашего BACKLOG; (3) комментарий-образец в `tmplatformctl` (`runs.go:45`) зовёт голый `tmctl` из PATH — копипаст воспроизводит тихий no-op СТАРЫМ бинарём при живом деадлоке, ваш же README:139-141 требует версионированный путь. Вопросы — через владельца.
## Пинг оркестратора №17 (второй) — 15.08.2026 (S4 принят, контракт 0.2.3 в каноне — пять строк вашего регистра протухли; аудит корпуса)
Фронт S4 принят и заленден (D39.135), контракт **0.2.3** в каноне. Для вашего регистра: (1) **PD-172 · PD-173 · PD-174 · PD-180 — диспозиция «спек-правка 0.2.3 задачей S4» (D39.130 п.2в) ИСПОЛНЕНА** — правила стоят в `openapi.yaml` §createBook (PD-172 при этом уточнён ПО ВАШЕМУ коду: чтение останавливается на файле, обязательное после файла = 400 «как не слали», необязательное молча теряется — D39.135 п.2б); обновите статусы. (2) **PD-199 ратифицирован закрытым ещё D39.132 п.2а** («null на проводе подтверждён»), в регистре до сих пор open — привести. (3) **Ваше НОВОЕ обязательство в P7** (D39.135 п.2в): читать `BookIntake.title` (сегодня падает в «unknown field is IGNORED», `v0.go:369-376`) и проецировать `books.reject_reason` на провод (сегодня «kept for an operator and never projected», `v0.go:497`) — без этого 0.2.3 остаётся обещанием без носителя. (4) Кандидаты в P7 по аудиту корпуса 15.08: **PD-219** (упавший дрейн теряет хвост `unit_resolutions` навсегда — зона сама вешала его на читающую поверхность) · **PD-217** (книга на вечном холде блокирует апгрейд движка — вторая половина после D39.132) · **PD-162** (удалённый каталог книги = вечный прогон с открытым холдом). (5) ⚠ Ваш же журнал честно фиксирует: «`tmctl migrate` живьём не гонялся» — команда теперь СУЩЕСТВУЕТ и принята (D39.134); мнение оркестратора: прогнать деплой-рантбук end-to-end на дев-стенде настоящим migrate — обязательное предусловие первого выката, дешевле любого пака. Вопросы — через владельца.
## Пинг оркестратора №17 (третий) — 16.08.2026 (контракт-ревью принято D39.138: ваша половина батча 0.3.0, PD-104 закрыт, попутные находки)
Контракт-ревью API v0 отработало отдельной сессией и ПРИНЯТО (отчёт — `docs/research/28-contract-review.md`; решения владельца — его §8, ратификация D39.138). ⚠ Читать ОРИГИНАЛ отчёта, не этот пересказ — он и есть носитель.
1. **PD-104 ЗАКРЫТ словом владельца 16.08** (§8 п.14): `SignupGrantMicroUSD`**0** на бете, начисление руками; возврат $5 — вместе с суточным агрегатным потолком, когда появятся платежи. Исполнение — P7-однострочник конфига + тест (грант при неверифицированном email уже 0, `login.go:306-308`). Обновите регистр.
2. **Ваша половина батча 0.3.0 — ПОСЛЕ лендинга спеки; состав — §5 отчёта:** `wireProgress` → один счётчик (Б-0; ваши колонки и словарь шва НЕ трогаются — фазы остаются внутренним делом); словарь машинных `code` + `request_id` в `Problem` (Б-1/§8а: класс 1 — конкретика максимальная, класс 2 модельный — ОДИН грубый код без вариации); `GET /capabilities` (Б-2: пары/порог интейка/форматы/страницы/версия — всё уже есть в конфиге и константах); `WWW-Authenticate` на 401 и две причины 403 (Б-15); трейлинг-части интейка → 400 вместо тихой потери + `Location` на 201 + `Idempotency-Key` (Б-3); подрезание `limit` до максимума вместо сброса к дефолту (Б-10, `books.go:509-511`); `PATCH /books/{id}` title-only + `DELETE` + `GET /runs/{id}` + `Run.failure_reason` (Б-5/Б-8 — словарь исхода у движка уже есть); `blocked: {code, book_id}` на run-options/409 (В-6); отказ по неподдерживаемой паре кодом на интейке (сегодня ja/en-книга умирает `failed`-ом на валидации конфига движка в КОНЦЕ пути — денег не сгорает, но причина не доезжает).
3. **Сеть (§5б; вход до первой живой книги под фронтом, строка 186):** gzip на текстовых ответах (НЕ на SSE) и `ETag`/`If-None-Match`→304 на списочных GET — ваше же требование `PLATFORM_DIRECTION.md:202-205`, батчем оно уезжает В КОНТРАКТ. Числа: дерево 2283 глав = 250 КБ на кадр `status`, фокус-рефетч 12 вкладок = 562 КБ.
4. **К-10 (пофазность у главы): НЕ СТРОИТЬ** — поправка приёмки: вердикт §6 отчёта («правка проекции») противоречит Б-0; фазы уходят с провода и у главы.
5. **Попутные находки §9** (сессия их адверсариально НЕ судила — проверьте у себя, заведите П/PD-строки по месту): (а) расчёт может висеть навсегда в двух легаси-путях (`reconcile.go:660-670` — попытка без базовой отметки; дев-путь без движка) — бюджета «сколько холд может висеть» не существует; (б) флаг «аккаунт остановлен» выводится сканом последних прогонов ВСЕХ книг (`pgstore/books.go:720-724`) — одна книга зажигает аккаунт; после PD-203 перечитать; (в) `ReadRunForSpawn` линеен по живым прогонам (`pgstore/runs.go:712`); (г) `/metrics` без аутентификации, защита — только 127.0.0.1 (`main.go:200-202`); (д) лимитер входа один на процесс (`login.go:128-129,165`) — один клиент упирает вход всем (выбор объяснён комментарием, следствие стоит записать); (е) сырой Go-текст ошибки лежит причиной карантина в БД (`reconcile.go:320`) — на провод не идёт. Вопросы — через владельца.
**Аддендум к пингу №17-третьему (16.08, свип планировочных доков D39.139):** (1) `docs/PLATFORM_DIRECTION.md` §2 и `BACKLOG.md` П-7 держат «фри-тир дефолт $5» — ПРОТУХЛО против PD-104 (слово владельца 16.08: грант 0 на бете, начисление руками; D39.138 п.2л) — поправить тексты при P7. (2) Баннер §3 PLATFORM_DIRECTION («ратификация за оркестратором») закрыт ещё D39.132 п.2б: oapi-codegen — кандидат при P7, sqlc — привязан к P7, River-факт поправлен; баннер снять/заменить ссылкой на ноту. (3) §4 п.5 («сервер ВПРАВЕ склеивать события») сузится батчем 0.3.0: склейка только снимкам, `note` не склеивать (research/28 Б-6в). Вопросы — через владельца.
**Дозакладка к §4.6 промта P7 (16.08, аудит готовности зоны; релей владельца — подтверди эхом):** к списку протухших текстов зоны добавь: `PLATFORM_DIRECTION.md` §4 п.8 («живого канала до-прогонных состояний нет») — решено книжным SSE 0.3.0, опрос запрещён нормой · §4 п.10 («персист манифеста — недостающий») — построен D39.122 · `:184` ссылка на несуществующий §3.3 стандартов · `platform/README.md:3-4` шапка «зона на P1» и «один тест-гейт» (гейта два) · `BACKLOG.md` П-17 — состав пака у́же промта (промт первичен). Расхождение ENGINEERING_STANDARDS:59 против DIRECTION:128 по oapi-codegen решаешь ты (промт §4.6). Всё — попутно, не отдельным заходом.
## Пинг оркестратора №18 — 20.08.2026 (P7 акт 5 ПРИНЯТ С ФИКС-ЛИСТОМ и ЗАЛЕНДЕН; контракт 0.4.0 ратифицирован; тело — D39.152/D39.153)
**Вердикт: ПРИНЯТЬ С ФИКС-ЛИСТОМ.** Панель шести линз в изолированных копиях (слепая · контракт-конформность · деньги · шов · вне карты · ревью канона 0.4.0 другой моделью) + пере-раны и собственные посадки оркестратора. Регрессов против HEAD нет, ни одной линзы с REJECT.
**Пере-проверено МОЕЙ рукой (заявление = команда):** `make check` с обоими гейтами — 18 пакетов, EXIT=0, **скипов 0**, линтер 0 issues · реестр 327 строк / 65 открытых (13 minor, 52 info), major и BLOCKER нет — `python3 docs/scripts/counts.py --check` · оба док-гейта зелёные · `tmplatformctl books --migratable` живой (exit 0, «resumable run» на стендовой книге) · миграционный манифест сходится, released-миграции не тронуты, комментарий про пере-отпечаток 00016 честный.
**Замер, обосновавший разворот 00016→00022, пере-выведен независимо** (свой бенчмарк на трёх вариантах в копии дерева): джойн **14.6 мс** · счётчик **5.3 мс** · без счётчика **2.9 мс**. Порядок и вывод подтверждаются, разворот законен. ⚠ Процент назван в ТРЁХ носителях тремя разными числами (журнал «83%», `perf_test.go:13` «96%», миграция 00022 «16.8 против 3.4, джойн сам по себе 9 мс»); мой замер даёт ≈80%, а «96%» ни из чего не выводится — один носитель на факт, остальные указывают.
**Собственные посадки мутаций ВНЕ вашего списка — 8, поймано 7:** долг не поставлен третьей концовкой ✅ · `edit_wave` не монотонен ✅ (⚠ сообщение пина печатает указатель вместо значения — `readmodel_test.go:918`) · снят `Vary: Accept-Encoding` ✅ · HEAD лишён валидатора ✅ · `reopen` снимает стоп банка кому угодно ✅ · погашение долга без сверки метки ✅ · вынос метки долга из закрывающей транзакции во второй оператор ✅ (техника `xmin` §36 работает, проверено исполнением).
**НЕ поймано — дыра:** снятие `structure_version` И `revision` из `emitFrame` (`pgstore/events.go:72-73`) проходит ВСЮ батарею. Это два поля, которые канон требует на КАЖДОМ кадре (`EventBase`), и ваш же комментарий это утверждает. По вашей норме PD-1 свойство без пинящего теста считается НЕ закрытым.
### Фикс-лист приёмки — завести строками СВОЕГО бэклога и регистра (зона моя не пишет)
**ПЕРВЫМ — не из пака P7, но найдено вторым рубежом приёмки и проверено мной построчно: `Sweep` останавливается для ВСЕЙ инсталляции на двух медленных прогонах, и выхода нет.** `cmd/tmplatformd/runner.go:189``sweepBudget = 2 * time.Minute` на ВЕСЬ проход; `internal/runs/reconcile.go:70``defaultRunBudget = 60 * time.Second` на ОДИН прогон. 120/60 = 2: два прогона, выбравшие свой бюджет, съедают проход целиком, и тогда `ctx.Err()` (`reconcile.go:39,48`) обрывает цикл — **`UnsettledRuns`, единственный ретрай отложенного расчёта, не вызывается вообще**, а живые прогоны, стоящие ниже, не реконсилируются и не спавнятся. `ListLiveRuns` сортирует `order by r.started_at` (`pgstore/runs.go:249`), то есть заклиненный прогон — по построению самый старый — стоит в голове и голодит остальных КАЖДЫЙ проход, детерминированно. Ручки нет: `runs.Config.RunBudget` объявлен (`internal/runs/runs.go:62-65`), но `startRunner` его НЕ присваивает (`cmd/tmplatformd/runner.go:74-83`), переменной окружения нет ни для одного из двух чисел, а `tmplatformctl` не умеет ни закрыть прогон, ни вернуть холд. Цена: прогон вечно `finished_at is null` ⇒ холд не возвращается ⇒ `runs_one_live_per_book` не даёт запустить новый прогон этой книги. Деньги заморожены, книга заморожена, пользователю видно «идёт».
**Достижимо буднично, без экзотики:** `RunSink.Apply` берёт транзакцию с блокировкой книги на КАЖДУЮ строку журнала (`pgstore/sink.go:80-104`), поэтому после часа простоя демона две книги с бэклогом выбирают проход целиком — и всё время догона деньги всей инсталляции не считаются.
**`PD-169` в вашем регистре стоит `fixed(P5)` и этим ЛЖЁТ приёмке:** его пин гоняет `Sweep` вообще без дедлайна прохода, то есть доказывает пер-прогонный бюджет, а не выживание прохода. Пере-открыть. Код приехал в P6 и в дифф P7 не входил (`Sweep` против HEAD — identical), поэтому ни одна диффовая линза его увидеть не могла: тот же класс «композиция известных фактов», что и строка 198. ⚠ Наблюдаемость при этом ЕСТЬ и она ваша: `tm_platform_sweep_unfinished_total` растёт — то есть оператор увидит, что проход не дошёл до конца, и не сможет ничего сделать. Метрика без ручки — половина механизма.
1. **`ContractVersion` = "0.3.0" при формах 0.4.0** (`internal/httpapi/capabilities.go:13`, он же в каждом `hello``stream.go:100`). Ратификация 0.4.0 состоялась (D39.152) ⇒ константа обязана подняться. Комментарий над ней сам это требует: «raised in the same commit as the code that implements a new minor». Сегодня практического вреда нет (фронт заморожен на 0.2.3), но деплой объявляет версию, которую не отдаёт. **Первым пунктом.**
2. **Пины на два поля `EventBase`** (см. выше). ⚠ Попутно 21.08 усилен гейт якорей доков: `file:line` теперь может нести токен ожидания (`` `путь:12-14`=`подстрока` ``), и такой якорь сверяется ПО СОДЕРЖИМОМУ на каждом прогоне, включая коммиты, которые двигают ЦЕЛЬ. Ваши доки (`platform/docs/**`) вошли в область линта — раньше их не сканировал никто.
3. **Бюджет попыток материализации.** У долга нет предела и канала «признать безнадёжным»: claim → fail → defer(`now()`) → следующий свип через 15 с, вечно (`pgstore/books.go:538,557-564`). У интейка предел есть (`parseAttempts`=5). Три следствия: до 5 мин движковых процессов на проход бесконечно · поток такой книги НЕ заканчивается никогда (`AtRest` требует `read_model_owed_at is null`, `events.go:125`) · при живом манифесте и падающем экспорте `SaveStructure` коммитится каждый проход ⇒ `revision++` и кадр каждые 15 с.
4. **sqlc — БЕРЁМ, слово владельца 20.08 («я вообще за»).** Отступление P7 закрыто, PD-44 переоткрыть исполнением. ⚠ Ваше предложение «взять на однооператорных ручках P8» покупает инструмент туда, где не болит: рантайм-ошибки «нет такой колонки» случились в СКЛЕЕННОМ SQL read-модели (⚠ счёт: проверяемый носитель — ваш регистр PD-44 и журнал — несёт ДВЕ; ещё две вы назвали в ответе владельцу, в доки они не попали, поэтому опираемся на две — испр. 20.08) (`chapters_before`/`stop_for_signing``readmodel.go:441,446`, там же `note_count` и сломанный алиас `b`), куда sqlc по построению не дойдёт. Тем же паком — гейт, который туда дойдёт: прогонять КАЖДЫЙ собранный запрос через разбор Postgres (prepare/describe) против мигрированной схемы; склейка ему не мешает, он получает финальную строку. И ответить попутно на вопрос, которого мы не знаем: **есть ли в read-модели запрос, которого не касается ни один тест** — если есть, это не «медленная обратная связь», а дыра. ⚠ **Половина ответа УЖЕ получена вторым рубежом приёмки** (собранный SQL извлечён из пакета через `go/types` и прогнан `EXPLAIN (GENERIC_PLAN)` против мигрированной схемы): **141 запрос, все планируются чисто, остаточных «нет такой колонки» НЕТ**; тестом ЭТОГО пакета недостижимы десять, но по батарее ЦЕЛИКОМ реально не покрыты только **три** (`DeleteOldLoginEvents` · `UserByIdentity` · `Observe`; остальные семь покрыты на 5887% тестами соседних пакетов — пере-мерено покрытием при аудите 20.08, первая редакция называла десять и это было неверно) — `RefundParseAttempt` · `StuckIntake` · `OpenReservations` · `ReleaseUnspawned` · `DeleteOldLoginEvents` · `UserByIdentity` · `Observe` · `SpendBound` · `AttemptReservationOpen` · `RunPausedReason`, и ни один из них не трогает схему 0001600024. То есть дыры сегодня нет, а гейт нужен как ПОСТОЯННЫЙ — именно он и делает этот ответ воспроизводимым.
5. **PD-297** — round-trip на строку в `SaveStructure` под эксклюзивной блокировкой книги; мерить на корпусной книге до и после (корпусная книга появится на холодном прогоне движка).
6. **Труба доставки решений банка в движок** — единый бэклог, строка **199(а)**: перед resume писать решения в `mined_delta`/`mined_rejects`. Сегодня `action`/`dst` — write-only колонки (единственный SELECT `readmodel.go:336` проверяет лишь наличие строки), а воркер описан в комментарии вашей же миграции `00002_readmodel.sql:172-174` и не построен.
7. **Мусор:** пустой `platform/ru` (0 байт, обрубок редиректа) — в лендинг не взят, снесён оркестратором. `books.go:234-235` — задвоенная первая строка доккомментария, класс PD-310/326.
8. **Ревью-пак четырёх осей, которых не смотрел НИКТО** (ваш же obstacle): деньги и леджер целиком · вход/сессии/CSRF · очередь и джобы · метрики. Это не дофикс и не довесок — первый взгляд, отдельной работой.
9. **Один носитель на факт для замера 00016→00022 — но НЕ удалением числа.** Выигрыш назван ЧЕТЫРЬМЯ носителями: шапка этого журнала «83%» · `internal/pgstore/perf_test.go:13`=`96% of the page` «96%» · миграция 00022 «16.8 против 3.4, джойн сам 9 мс» · регистр PD-306 (повторяет 83% и 16.8/3.4). ⚠ Я сперва записал, что «96%» ни из чего не выводится — **это была моя ошибка, снята проверкой записей**: оно выводится точно из ВАШЕГО же замера в `archive/P7_ACT5_FIX_PLAN_2026-08-20.md:444-446`=`636 мс против` («636 мс против 24 мс», холодный корпус акта 4) = 96.2%, тогда как 83% и мой независимый ≈80% — с вакуумированного корпуса. То есть носители меряли РАЗНОЕ и все честны. Свести указанием УСЛОВИЙ замера при каждом числе, оставив нормативным один (миграция 00022 — она их и несёт); удалять «96%» как фантом НЕЛЬЗЯ.
10. **У материализатора нет ПОЛА на пустой манифест — латентная потеря всего текста книги.** `internal/readmodel/readmodel.go:215-232` строит `in.Chapters` только из `manifest.Chapters` и ни разу не сверяется со счётчиками того же документа (`ChaptersTotal`/`UnitsTotal` лежат рядом и печатаются в лог строкой ниже). Пустой список едет в `SaveStructure`, где `pgstore/readmodel.go:193-196` выполняет `delete from chapters where book_id = $1 and not (id = any($2))` — на пустом массиве предикат истинен для ВСЕХ глав, и каскад `chapters → units` сносит текст; при пустом `Key` вдобавок срабатывает `changed` и уходят все `unit_resolutions` (родня строки 198). Сегодняшним движком недостижимо (`buildManifest` всегда наполняет главы, файл пишется атомарно) ⇒ фикс-лист, не блокер. **Асимметрия и есть находка:** на ИНТЕЙКЕ ровно этот случай отловлен явно и прибит мутацией (`internal/books/parse.go:134-142`, `books_test.go:1296`) — «нет глав, но работа считается» там не признаётся правдой о книге. У материализатора такого пола нет, и теста на нулевой манифест в `readmodel_test.go` тоже нет. Лечится одной сверкой `len(manifest.Chapters)` против `manifest.ChaptersTotal`.
11. **`PD-327` в регистре стоит `open`, а канон 0.4.0 РАТИФИЦИРОВАН** (D39.152) — то есть условие её закрытия наступило. Закрыть строку регистра явно; сегодня расхождение видно скриптом (`counts.py --check` печатает PD-327 среди открытых), и это ровно тот класс, ради которого регистр объявлен источником истины по статусу.
12. **Изоляция читающих чтений НЕ ЗАПИНЕНА (аудит 21.08, посадка мутации).** Снятие `RepeatableRead`+`ReadOnly` у `inReadTx` (`internal/pgstore/credits.go:492-493`) проходит ВСЮ батарею — при том, что под этим инвариантом лежат шесть ручек выдачи, а носитель прямо объясняет цену («every frame in that window was lost for good», `books.go:684-688`, PD-163). По вашей же норме PD-1 свойство без пинящего теста считается НЕ закрытым — а это тот самый класс, которым я мерил найденную дыру с `emitFrame`. Код приехал НОВЫМ в P7, то есть лежал внутри диффа, который читали шесть линз приёмки и я сам.
13. **`CreditHeldBy`: оговорка `and book_id <> $2` не запинена (аудит 21.08, посадка мутации).** Мутант, снимающий её, проходит батарею: пин, который выглядит покрывающим, её ни разу не исполняет — в фикстуре у исключаемой книги холда НЕТ (`internal/pgstore/credits_test.go:443-465`). Сумма кормит контрактный `blocked`, и раздутая сумма отправляет пользователя гасить прогон, который ничего не освободит — ровно тот вред, против которого написан комментарий над самим запросом (`credits.go:393-397`).
### Что ушло в ЕДИНЫЙ бэклог (движок/шов/контракт — ваши строки туда не заходят, D39.84)
**198** апгрейд движка стирает замечания и счётчики безвозвратно (ваша зачистка при ре-кате × announce-once движка — композиция, гейт холодного прогона) · **199** канал доставки правок банка · **200** сквозная полоса прогресса вместо пофазной (слово владельца 20.08) · **201** «Глава N» внутри текста экспорта · **202** живой прогон насквозь через API — ПОСЛЕ холодного прогона движка (слово владельца 20.08) · **203** хвосты контракта · **204** движок публикует причины флагов данными (релей §7в, ваш PD-246 — строка заведена в бэклоге ДВИЖКА, как вы и просили). ⚠ По релеям §7 сверено грепом при лендинге, а не по вашему списку: **(б) и (д) уже ИСПОЛНЕНЫ** синком 0.4.0, **(г) наполовину** (`unspecified` ратифицирован, открыта граница ступеней), **(и)** закрыт полем `stop_requested`. Реально открыты только (а)-канон-половина, (з) и договорная часть (к).
### Ратификации, которые вас касаются
**Контракт 0.4.0 РАТИФИЦИРОВАН (D39.152)** — PD-327 закрывается лендингом канона, он состоялся. ⚠ Ваш клейм «ломающая правка ровно одна» верен для диффа генерённых ТИПОВ и неточен поведенчески: против 0.3.0 расходятся ПЯТЬ мест — пятое, семантика `410 Gone`, принесена проверкой записей и названа вашим же PD-253 (`stop_requested` · тождество интейка по содержимому против дословного «never over the bytes themselves» · `409` там, где таблица резюма говорит `202` · валидатор на двух ручках сверх объявленных). Все четыре 0.4.0 благословляет, поэтому цена уплачена ратификацией — но клейм в отчёте стоит поправить, чтобы следующая приёмка не опёрлась на него.
**Оговорка про пер-термные решения расширена на ВСЮ ручку** (была только на `decline`): инертны одинаково и `approve`, и `dst`. Оговорка временная — снимается исполнением строки 199(а).
**Исправление МОЕГО же пинга №15 (испр. оркестратором №18):** «Все сайдкары пишутся атомарно (temp+rename)» — НЕВЕРНО. Атомарны три из шести (`manifest.json`, `bank.json`, `bank-stop.json``backend/internal/pipeline/artifact.go:24-59`); `bank-stop.txt`, `mined-signature.yaml`, `auto-bank.yaml` пишутся `os.WriteFile` с усечением. Сегодня безвредно (читателей нет), но сессия, взявшая любой из них по той строке, получит усечённый документ на живом прогоне.
**Сессия P7 ЗАКРЫТА владельцем 20.08.** Промт отработан → `platform/docs/archive/`. Дальше — другие сессии.