# Промт: платформа, пак P7 — читающая поверхность по контракту 0.3.0 + модель ошибок + форварды (П-17, строка 169 единого) > Выдан №17 16.08.2026, пересобран после лендинга батча 0.3.0 (D39.142–144) и опровергателя промта. > Запуск — по слову владельца. ## 0. Зачем Платформа (control plane фронт↔движок) построена до потребительской половины шва: вход, кредиты, раннер, интейк, ингест событий движка в Postgres. Из 20 операций контракта 0.3.0 отвечает на 8: фронт не может читать главы/юниты/замечания/банк/поток — он на моках и ЗАМОРОЖЕН, экран подписи строить не на чем; отказы — английской фразой без машинного кода. Твой пак делает платформу читаемой и отказы машинными → разблокирует фронт. Контракт только что пере-согласован и сломан минором 0.3.0, чтобы ты строил СРАЗУ по правильной форме — не по памяти о старой. ## 1. Нормативный источник **Канон `docs/architecture/14-api-contract/openapi.yaml`, `info.version: 0.3.0`** (не та версия → стоп, пинг). Зеркало `frontend/docs/api-contract/` ОТСТАЁТ намеренно (фриз фронта) — читать только канон. Форма каждой ручки решена там; расхождение промта с каноном → верь канону и пингуй. ## 2. Зона и git Ты не коммитишь — лендит оркестратор. Пишешь только в `platform/`; отчёт/вопросы — только `platform/docs/platform-PROGRESS.md`. Чужие зоны read-only; живой полигон (`eval/`, `docs/experiments/`) не трогать; `.env` не читать; git не трогать вовсе (ни `add`, ни `commit`, ни `reset`/`checkout`), файлы застейдженными не оставлять. Путь Go-модуля не вкладывать под путь движка (D39.85). ## 3. Читать (≤5) 1. Канон + компаньон (`docs/architecture/14-api-contract/`) целиком. В компаньоне: §3 таблица «чтение → источник» · **приложение А-2 — готовая карта «твоя ветка `v0.go` → код ошибки» с `file:line`** · §6б — почему формы такие. 2. `docs/research/28-contract-review.md` §8а (классы отказов) · §5б (сеть) · §9 (находки твоей зоны) + D39.138 п.2 и D39.142–144 (хвост `docs/architecture/05-decisions-log.md`). 3. Свой журнал `platform-PROGRESS.md`: три пинга №17 + аддендумы (входы пака, `file:line` находок). 4. Read-out движка — пинг №15 там же: `.manifest.json` (`tm-manifest-v2`; `unit.id = ::`; ⚠ `heading` — ВРЕМЕННЫЙ рендер «Глава N», НЕ метка книги) · `.bank.json` · `.bank-stop.json` (`conf: null` ≠ 0); пишутся атомарно, читать можно при живом прогоне. Плюс своя `unit_resolutions` (миграция 00015). 5. `CLAUDE.md`; жаргон — `docs/glossary.md`. ## 4. Состав. Пункты 0–5 — «РОВНО так» (состав ратифицирован; формы внутри — твои, где канон/промт их не фиксируют). Пункт 6 — судишь сам. Отложенное — только явно, списком в отчёте. **0. Рантбук деплоя живым `migrate`, end-to-end, $0, первым шагом** (предусловие первого выката; твой журнал: «живьём не гонялся»): прогнать свой `deploy/README.md` новым бинарём движка по книгам стенда (`tmplatformctl books --migratable` → `tmctl migrate`). Попутно: `deploy/README.md:131` — мёртвый текст ошибки (движок печатает `schema_mismatch found=N expected=M`, exit 13); `cmd/tmplatformctl/runs.go:45` — комментарий зовёт голый `tmctl` из PATH (тихий no-op старым бинарём); якорь П-1 в BACKLOG обновить. **1. Форварды:** (а) читать `BookIntake.title` — сегодня игнорируется (`internal/httpapi/v0.go:369-376`); по 0.3.0 обязателен, пустая строка = «назови по файлу»; (б) проецировать `reject_reason` — писатель `pgstore/books.go:310`, проекция `projectBook` (`v0.go:470`; обоснование-комментарий `:497`) — значениями словаря канона `RejectReason` (`processing_failed`, не имя внутренней ветки). **2. PD-104:** `SignupGrantMicroUSD` дефолт → **0** + тест (слово владельца 16.08; возврат $5 — с суточным потолком при платежах). Ветка «0 при неверифицированном email» (`login.go:306-308`) уже есть — не баг. Протухшие «$5»: `PLATFORM_DIRECTION.md` §2, BACKLOG П-7, регистр. **3. Модель ошибок — ПЕРВОЙ из контрактной половины** (от словаря зависят формы остальных ручек): `Problem` по канону (`code` 16 корневых + `cause` + `request_id` из `internal/reqid` + `errors[]`). Словарь НЕ выводить — реализуй карту А-2 компаньона; реальная ветка без кода → пинг, не изобретение. Класс 2 = один код `content_refused`, без вариации, лимит попыток — по канону (`:2394`). `WWW-Authenticate` на 401. Две причины 403 (нет `X-TM-Client` ≠ кросс-origin) — раздельно только в открытом `cause.code`, корневой `forbidden` один (канон), новых корневых не заводить. **4. Читающая поверхность (ядро):** `listChapters` · `listUnits` (только пер-главно; ручку «вся книга юнитами» НЕ заводить — инвариант твоего же `PLATFORM_DIRECTION.md:208-210` и §5б; `410 Gone` на исчезнувшую главу; `Unit.notes` массивом) · `listNotes` (`id`+`created_at`+обязательный `chapter_id`; порядок `created_at`, тай-брейк серверный) · `listBankTerms` (проекция сайдкаров; агрегаты `total`/`signed`/`pending_decisions`/`complete` — первая страница, ИНФОРМАЦИОННЫЕ) · `submitBankDecisions` (`approve`/`decline`, накопительный, `maxItems` 1000; неизвестный `term_id` → 400 всей пачки, item-код `unknown`) · дельта-чтения `?after_version=` на notes/bank (ВКЛЮЧИТЕЛЬНЫЕ; водяной знак = наименьшая ревизия обхода; устаревший → 400 `version_too_old`) · **SSE `streamBookEvents`** — все правила в каноне: `id` = позиция истории книги (служебные кадры несут id последнего кадра, `0` на пустой книге), кадр `end` + `204` по `Last-Event-ID` (кадра «конец разбора» НЕТ — конец интейка = смена статуса), склейка только state-кадрам, `note` не терять в пределах соединения, `resync_required` когда не можешь продолжить с предъявленного id · **`structure_version`** — в `Book`, во всех четырёх конвертах страниц и в КАЖДОМ кадре (`EventBase`); источник — манифест движка · **gzip** на JSON (НЕ на SSE) + **ETag/If-None-Match → 304** на всех коллекциях, КАРТОЧКЕ книги и `/capabilities`; валидатор — ко всему запросу, query включая (`:850` канона; «ETag = ревизия страницы» НЕДОСТАТОЧЕН — стр.2 и дельта той же ревизии обязаны отличаться). ⚠ Ловушки, каждая — канон против сегодняшнего кода: - **`heading`:** канон запрещает класть рендеренный порядковый (`:1556-1566`) — а `heading` манифеста ровно он; проецируй `null` до движковой строки 160. Прямая проекция сайдкара = нарушение. - **Гейт полноты банка УЖЕ ПОСТРОЕН — ДЕМОНТИРУЙ:** `internal/runs/reconcile.go:934-938` (`awaiting_bank` → `ErrNotResumable "glossary is not signed"`) ссылается на снятые D39.144 разделы. Подпись = ОДИН resume при любом состоянии решений; полноту не отливать ни в новый код, ни оставлять в старом. Границу паузы движка сверь чтением его кода (строка 191); расхождение — пинг. - **Пофазность у главы НЕ строить** (К-10 «НЕТ», D39.138 п.5): колонки `units_draft_done/edit_done` лежат готовыми и напрашиваются — глава отдаёт ОДИН счётчик прохода по канону. - **`Note.code`:** словарь НЕ ратифицирован (приложение А — заготовка ◆). Бери коды из ◆-карты как есть, новых не изобретай; причина движка мимо карты → пинг. **5. Конформность ПОСТРОЕННОГО. Правило: пофайловый дифф каждой построенной wire-структуры против required-списка канона; расхождение — чинить или в отчёт с причиной.** Именованный минимум: `Progress` переезжает с книги на `Run` (одна полоса в ГЛАВАХ до остановки, знаменатель — купленный объём) · `Book`: +`revision`, +`chapters_done` (SQL уже считает — `pgstore/books.go:677-681`), −`progress`, **−`genre`** (`v0.go:108` — нарушение allowlist-инварианта канона, строка 184) · `Run`: +`book_id`, +`failure_reason` (словарь канона из трёх значений — мапь полосу 10–19 и ветки `outcome()`), `verify_bank`→`stop_for_signing` (провод: `v0.go:127,154`) · `Usage`: `paused_reason`→`halt_reason` со СВОИМ словарём `AccountHaltReason` (`v0.go:159-163` сейчас несёт прогонный) · `GET /capabilities` — НОВАЯ ручка и зависимость всех чтений (дефолты страниц читаются из неё; все шесть полей канона, `intake_enabled` включая) · `limit`: подрезать до 1000, НЕ отвергать (`pgstore/books.go:510-511` сейчас сбрасывает к дефолту; схемную валидацию параметра вывести из-под отказа — `:886` канона) · трейлинг-части интейка → 400 (⚠ меняет узаконенное 0.2.3 «молча теряется» — по канону 0.3.0) · `Location` на 201 · `Idempotency-Key` на `createBook` И `startRun` — семантика целиком в `§IdempotencyKey` канона · `blocked {code, book_id}` на run-options и 409 (таблица `reservations` знает держателя) · отказ по неподдерживаемой паре кодом на интейке · `finalizing` — вон из словарей валидации (`internal/runs/runs.go:229`, `pgstore/sink.go:435`; CHECK-констрейнты — по канону, значение никем не пишется). **6. Свой регистр — судишь сам, кроме помеченного РОВНО:** РОВНО — статусы PD-172/173/174/180 (исполнены 0.2.3) и PD-199 (закрыт D39.132 п.2а) привести; строку PD-201 обновить (движковая половина финальна — самолечение «exit 13 → migrate → повтор» можно строить); **PD-241 исполнить — ратифицирован D39.132 п.2д «следующим касанием зоны», это касание — ты** (намерение стопа выигрывает на статусе). Судишь: кандидаты PD-219/PD-217/PD-162; находки §9 research/28 — каждой диспозицию (берёшь / P8 / отклоняешь, с причиной): висящие расчёты без бюджета холда · флаг аккаунта сканом всех книг · `ReadRunForSpawn` O(живых) · `/metrics` без аутентификации · лимитер входа на процесс · сырой Go-текст в карантине. Протухшие тексты зоны: `PLATFORM_DIRECTION.md` §3-баннер (снят D39.132 п.2б), §4 п.5 (склейка сузилась), **`:199` «подписывает банк частями» — ложь после D39.144**, §2 «$5». Инструменты: **sqlc — привязан к паку** (D39.132 п.2б; `overrides` на денежные типы; отступление = пинг); oapi-codegen — кандидат, реши и аргументируй. **Отложено в P8 (не тихо):** `updateBook`/`deleteBook`/`getRun` · **`createExport`/`getExport`** (содержимое — S7; формы в каноне финальны) · эскроу П-18. Всё прочее невлезшее — списком с причинами. ## 5. Нормы - Самопроверка исполнением: `make check` + ОБА гейта (`TM_PLATFORM_TEST_DSN`, `TM_PLATFORM_TEST_ENGINE_BIN`/`_BOOK_TEMPLATE`), **скипов 0 — гейт** (неустранимый скип = obstacle с объяснением); дифф `^func Test` исполнением; живые пробы новых ручек на дев-стенде (сид P6). - Заявление = команда: каждое число/категорика отчёта — с командой; приёмка пере-ранит. - Записка-план по §4 (пп. 0–6 + отложенные): «исполнено, где / отказ, почему» на каждый; сдача = все с диспозицией. - Шов: касаешься канала движка → сначала инвентарь ВСЕХ его каналов и кодов чтением чужого кода (урок D39.132). - Посадки мутаций — `ENGINEERING_STANDARDS.md` §3, списочный пункт «посадки» (~:67-69; заголовка «§3.3» в файле нет). - Предметная ось: индустриальный первоисточник прежде велосипеда — RFC 9457 · WHATWG SSE · RFC 9110/9111 (условные запросы, `Vary` при gzip). Право заменить ось — с аргументом. - Деньги: пак их не двигает (PD-104 — константа); коснулся — два независимых пути счёта из сырого леджера. - **Адверсариальное селф-ревью финалом — механизмом (субагенты РАЗРЕШЕНЫ, спавнь):** ≥2 независимых ревьюера по финальному диффу, author≠reviewer: (а) контракт-конформность — канон против твоих ручек, пере-раном живых проб; (б) деньги/шов — свой инвентарь каналов, не твой список. Плюс СВОИ посадки в новые гейты/ручки (не из ожидаемого тобой списка). Артефакт: таблица находок/посадок с диспозициями; ноль находок = не верить прогону. - Obstacle reporting — обязательная секция: что НЕ удалось / не проверено. - Канал вопросов: непонятно / промт против кода-спеки-доков → пинг через владельца, НЕ интерпретация. Право «этого делать не надо» — с аргументом. ## 6. Старт и сдача Эхо-протокол: первым деливераблом ≤10 строк «скоуп / инварианты / не-делать». Сдача: отчёт в `platform-PROGRESS.md` (записка-план · команды · живые пробы · таблица селф-ревью · obstacle · диспозиции §4.6), дерево незакоммиченное, регистру — строки по находкам. Без саги.