# Промт: платформа, пак P8-REVIEW — четыре оси, которых не смотрел НИКТО (read-only) > Выдан №18 21.08.2026 после лендинга P7 (D39.153). Запуск — по слову владельца. > ⚠ Это пак ЧТЕНИЯ. Кода он не меняет. Найденное чинит СЛЕДУЮЩИЙ кодовый пак по промту оркестратора (пак P8-FIX уже отработан и заленден — `archive/PLATFORM_P8_FIX_SESSION_PROMPT_2026-08-21.md`). > > ⚠ **ОБНОВЛЕНО 22.08 при лендинге P8-FIX (D39.154): порядок вышел ОБРАТНЫМ.** Этот промт писался в > расчёте, что читающий пак идёт ПЕРВЫМ и отдаёт находки дофиксу релеем; на деле дофикс отработал и > заленджен раньше (`31f1f82`). Что это меняет для тебя — три вещи, и ни одна не отменяет пак. > **(1) Ось 3 читает уже ПЕРЕПИСАННЫЙ код:** блокер закрыт четырьмя механизмами (фазы прохода · > отсрочка с бэкоффом · порог `StalledAfter` · `run abandon`), константы переехали в > `internal/config`, появились `RunsToReconcile`/`DeferRun`/`ClearRunDeferral` и миграция 00025. > Абзац про найденный блокер ниже оставлен как описание НАЙДЕННОГО — зона пере-нацелила его якоря и > сказала об этом прямо. Ищи сиблингов класса, но теперь И в самом лечении: оно свежее, его смотрели > ревьюеры зоны и приёмка, и у него уже есть четыре открытых строки — `PD-371`…`PD-374`. > **(2) ⚠ Поправка к первой редакции этого баннера (её ошибку нашёл аудит доков в тот же день):** > «оси 1, 2 и 4 не тронуты» — НЕВЕРНО. Ось 2 (вход · сессии · CSRF) действительно не тронута ничем, там > ты первый взгляд целиком. А оси 1 и 4 дофикс ЗАДЕЛ точечно, и ты обязан это знать, чтобы не искать > уже найденное: `internal/metrics/metrics.go` (+гейдж `runs_stalled`) и `internal/pgstore/credits_test.go` > (+пин `PD-334`), плюс `internal/pgstore/isolation_test.go` (`PD-333`). Системного разбора ни та, ни > другая ось не получила — точечные находки при постройке это не он. > **(3) §4.7 работает В ОБРАТНУЮ СТОРОНУ:** релея тебе ждать не от кого, твои находки поедут в > СЛЕДУЮЩИЙ кодовый пак — тем же протоколом, через оркестратора, с эхо-подтверждением. ## 0. Зачем Платформа — control plane между веб-фронтом и движком перевода книг: пользователи, кредиты и холды, очередь, HTTP API, запуск движка процессами. Она построена и заленджена, батарея зелёная. Но **ни одна из четырёх её осей не получила СИСТЕМНОГО разбора**: деньги и леджер целиком · вход, сессии и CSRF · очередь и джобы · метрики. Не потому, что они хороши, а по построению: каждая приёмка смотрела ДИФФ очередного пака. ⚠ Формулировка «ни в один дифф не входили» — прежняя редакция, и она была слишком сильной (испр. 22.08): кусками они в диффы входили при постройке, и точечные находки по ним в регистре ЕСТЬ. Ищи не «первое касание», а то, чего пер-пакетный взгляд по построению не видит: свойства, живущие МЕЖДУ паками. 21.08 первый серьёзный взгляд на ОДНУ из них — очередь — дал жёсткий блокер выката: двух медленных прогонов достаточно, чтобы расчёт денег и допуск новых прогонов встали для всей инсталляции, и ручки, чтобы выйти, нет. Он прожил два пака, и **в регистре был помечен закрытым**. Твой результат — находки с воспроизведением. Не правки. Их возьмёт следующий пак и починит разом, а не двумя заходами. ## 1. Нормативный источник и одна оговорка к нему **`platform/docs/DEFECT_REGISTER.md` — источник истины по СТАТУСУ каждого дефекта.** Открытые печатает `python3 docs/scripts/counts.py --check` (от корня репозитория). ⚠ **И он умеет врать.** `PD-169` стоял `fixed(P5)`, пока дефект был жив: его пин доказывал свойство СЛАБЕЕ, чем читается — гонял свип вообще без дедлайна прохода, то есть мерил пер-прогонный бюджет, а не выживание прохода. Поэтому строка `fixed` — это КЛЕЙМ, а не факт: если ось, которую ты смотришь, опирается на закрытую строку, открой её пин и проверь, то ли он доказывает. ## 2. Зона и git Пишешь только в `platform/`. Отчёт и вопросы — `platform/docs/platform-PROGRESS.md`; строки находок — `platform/docs/DEFECT_REGISTER.md` (это твой файл). **Ты не коммитишь** — лендит оркестратор (канон — `CLAUDE.md`, таблица зон). Чужие зоны read-only; живой полигон (`eval/`, `docs/experiments/`) не трогать; `.env` не читать; git не трогать вовсе — ни `add`, ни `commit`, ни `reset`/`checkout`/`restore`/`clean`/`stash`/`rebase`/`push`/`merge`/`switch`/`rm`/`mv`; файлы застейдженными не оставлять. ⚠ В дереве живёт НЕЗАКОММИЧЕННАЯ работа полигона (состав НЕ дублируем здесь — он менялся под руками уже трижды; смотри пинг оркестратора в `docs/PROGRESS.md`, секция «Полигон», и сверяй командой `git status --short -- eval/` ПЕРЕД работой) — не трогать и не «прибирать». Индекс на 22.08 чист (прежняя редакция писала «частично застейдженная» — испр.), но полигон-сессия жива и стейдж может появиться в любой момент. **⚠ РОВНО: кода не менять.** Ни одной правки `.go`, ни «заодно поправил очевидное». Причина не бюрократическая: правка, внесённая тем же, кто нашёл, лишает следующий пак независимой проверки, а тебя — чистого измерения «сколько тут дефектов». Исключение одно — временные правки в КОПИИ дерева ради посадки мутаций (см. §5), в репозиторий они не возвращаются. ## 3. Читать (≤5) 1. **`platform/docs/DEFECT_REGISTER.md`** — открытые строки целиком; закрытые по твоим осям — выборочно, с оговоркой §1. 2. **`platform/docs/STACK_DECISIONS.md`** — правила, которые не выводятся из одной функции: §22 (порядок блокировок), §33–36 (долг материализации, `edit_wave`, техника `xmin`), раздел «Стенд разработчика» (сборка, ОБА гейта батареи, грабли). 3. **`platform/docs/platform-PROGRESS.md`** — шапка (что построено и зачем) + **пинг оркестратора №18** в хвосте: там фикс-лист приёмки P7, и его **пункт 8 — это ты**. 4. **`platform/docs/ENGINEERING_STANDARDS.md`** §3 — норма посадок мутаций зоны. ⚠ Твой продукт — это ДЕСЯТКИ новых `file:line` в регистре, а гейт якорей с 21.08 видит `platform/docs` и умеет сверять по СОДЕРЖИМОМУ: `python3 docs/scripts/counts.py --lint --from-index` (от корня, ПОСЛЕ `git add` своих путей). ⚠ Именно `--from-index`: правило «на затронутых строках токен ТРЕБУЕТСЯ» живёт только на дельте индекса, поэтому прогон по рабочему дереву покажет ЗЕЛЁНОЕ там, где индекс краснеет — автор промта попался на этом сам 23.08, на двадцати девяти якорях сразу. Якорь может нести токен ожидания — `` `путь:12-14`=`подстрока` `` — и на строках, которые ты трогаешь, токен ТРЕБУЕТСЯ. Токен списывается из цели дословно, не по памяти: автор этого промта на этом попадался трижды за сутки. 5. **`CLAUDE.md`**; жаргон — `docs/glossary.md`. Всё остальное — код, и его ты достаёшь САМ: `file:line` ниже — отправные точки, а не пересказ. **Код первичен**: если док и код расходятся, верен код, а расхождение — находка. ## 4. Состав: четыре оси. Скоуп — «делай РОВНО так», глубина и метод — «реши сам» **РОВНО:** осей ровно четыре, они ниже; кода не менять; у каждой находки — воспроизведение. **Решаешь сам и аргументируешь:** порядок осей, глубина по каждой, сколько и куда сажать мутаций, звать ли субагентов и как делить между ними. Всё, что ниже помечено «приор» — опровергается аргументом или замером, это не задание. ### Ось 1 — ДЕНЬГИ И ЛЕДЖЕР ЦЕЛИКОМ (приоритетная) Приор: здесь дефект стоит денег. ⚠ **Уточнение базовой линии:** «не смотрел никто» — про СИСТЕМНЫЙ разбор оси; точечно её трогали, и в регистре есть строки со ссылками на `credits.go` и на прежние денежные линзы. Смотри их как известное, не переоткрывай. ⚠ **Два денежных дефекта, которые прежняя редакция этого промта звала «найденными, но не заведёнными», ЗАВЕДЕНЫ И ЗАКРЫТЫ паком P8-FIX 22.08** (испр. при лендинге): изоляция `inReadTx` — `PD-333`, оговорка `book_id <> $2` в `CreditHeldBy` — `PD-334`; у обоих пины, и оба пина проверены посадкой мутации. **Не переоткрывай их — ищи их КЛАСС**, и вот он сформулирован: «оговорка в комментарии или в SQL, которую ни один тест не исполняет». Тот же пак закрыл этот класс ещё в двух местах и оставил его открытым в третьем (`PD-372`: пин стоит на функции `truncateReason`, а ни один из четырёх её вызовов не проверен) — это живой образец того, что ты ищешь. Предмет: резерв → расчёт → возврат холда · потолки (книжный и дневной) · арифметика микро-долларов и округления · `spend_baseline`/`SpendBound` · кэп расхода холдом · путь «расход провайдера случился, а строка в леджер не легла» · `adjust` и доля в `/usage`. Отправные точки: `internal/pgstore/credits.go`, `internal/money/`, `internal/runs/reconcile.go` (`settle`, `restart`), миграции `00007_credits.sql`, `00010_spend_baseline.sql`, `00011_ceiling_argument.sql`. ⚠ Испр. оркестратором №19 23.08: прежняя редакция звала здесь `00019`, но это `00019_read_model_debt.sql` — долг read-модели, к деньгам отношения не имеет; ось 1 по ней ушла бы не туда. **Норма, обязательная к исполнению (канон приёмок).** ⚠ Обязателен САМ ЗАМЕР, а не оформление уже найденного: сессия, ничего не нашедшая, чисел не производит — и норма «каждое число двумя путями» не связала бы ничего. Поэтому минимум: сведи на стенде баланс аккаунта, сумму леджера и открытые холды двумя путями ДО всякого поиска — расхождение здесь и есть первая находка. Дальше каждое число — из СЫРОГО леджера **двумя независимыми путями** (чтение кода/SQL и прямой `SELECT` по стенду). Пути обязаны сойтись или расхождение называется числом. ⚠ И вторая половина той же нормы, без которой первая обманывает: **леджер = НИЖНЯЯ граница**. Сходимость двух путей доказывает согласованность, а НЕ полноту: вызов, за который провайдер списал, а строка не легла, сойдётся в обоих путях одинаково неверно. Для оси, у которой этот путь прямо в предмете, ищи его отдельно. ### Ось 2 — ВХОД, СЕССИИ, CSRF Приор: приёмка P7 смотрела на этой оси только владение ресурсами — кто чью книгу читает. ⚠ Носитель этого утверждения — obstacle-секции отчётов панели приёмки, которые в репозиторий не попали; в дереве его нет, и как факт он не проверяем. Считай это ориентиром, а не установленным покрытием: если найдёшь следы более глубокой проверки — тем лучше, скажи об этом. Предмет: генерация и сравнение токена сессии · ротация при логине · атрибуты куки и их граничные значения · привязка CSRF-токена · OIDC `state`/`return_to` · абсолютный и idle-потолок сессии · журнал входов. Отправные точки: `internal/auth/`, `internal/login/`, `internal/pgstore/sessions.go`, `internal/pgstore/identity.go`. ⚠ В регистре по этой оси уже есть открытые строки (`PD-6`, `PD-86`, `PD-87`, `PD-88`, `PD-101`, `PD-103`) — они ИЗВЕСТНЫ, повторно их не открывай; их наличие означает, что ось трогали точечно, а не смотрели. ### Ось 3 — ОЧЕРЕДЬ И ДЖОБЫ ⚠ **Блокер здесь УЖЕ НАЙДЕН — не ищи его заново, ищи его СИБЛИНГОВ.** Найденное: бюджет прохода свипа 2 минуты против бюджета одного прогона 60 секунд (⚠ **пак P8-FIX это ПОЧИНИЛ 22.08 и оба числа переехали** — теперь это дефолты операторских переменных `internal/config/config.go:433`=`SweepBudget: 2 * time.Minute` и `internal/config/config.go:434`=`RunBudget: 60 * time.Second`, пер-прогонный дефолт пакета — `internal/runs/reconcile.go:280`=`const defaultRunBudget = 60 * time.Second`; прежние якоря `runner.go:189` и `reconcile.go:70` мертвы, абзац оставлен как описание НАЙДЕННОГО, а не текущего кода) ⇒ двух медленных прогонов хватает, чтобы проход кончился до `UnsettledRuns` — единственного ретрая отложенного расчёта; `ListLiveRuns` сортирует по `started_at`, поэтому заклиненный прогон стоит в голове и голодит хвост детерминированно; ручки нет — `runs.Config.RunBudget` объявлен и в проде не присваивается. Приор: это класс «арифметика двух констант в разных пакетах, ни одна не покрыта тестом». Ищи такие же: где ещё поведение задаётся парой чисел, живущих врозь, и где у функции нет ни одного теста. Предмет шире: River-задания и их повторы · идемпотентность обработчиков · крэш-окна между сигналом и записью · свипы (интейк, идемпотентность, материализация) и их взаимное голодание. Отправные точки: `internal/jobs/`, `internal/runs/reconcile.go` (`Sweep`), `cmd/tmplatformd/runner.go` (`sweep`, `pass`). ### Ось 4 — МЕТРИКИ Приор: самая дешёвая из четырёх, и её острый вопрос **ПЕРЕВЕРНУЛСЯ после P8-FIX** (испр. при лендинге 22.08). Прежняя редакция звала его «наблюдаемость без ручки»: `tm_platform_sweep_unfinished_total` рос, а сделать оператор не мог ничего. Ручка построена — гейдж `tm_platform_runs_stalled`, `tmplatformctl runs --stalled`, терминальный `run abandon`. **Новый вопрос: ДОСТАТОЧНА ли она.** Видно ли по одной только `/metrics` до и после отказа, что прогон перешёл в `stalled`; различает ли оператор по гейджу и списку те случаи, где `run abandon` ОТКАЖЕТ (попытка ещё называет юнит или несёт базовую линию траты); и не появилась ли метрика, которая теперь растёт молча. Предмет: что вообще можно понять по метрикам в момент отказа · есть ли метрика, растущая молча · кардинальность лейблов · соответствие практикам именования, объявленным зоной (`STACK_DECISIONS` §24). Отправные точки: `internal/metrics/`, `cmd/tmplatformd/main.go`. **Механизм — исполнением, не чтением:** подними дев-демон по рецепту стенда, сними `/metrics` в покое, ВЫЗОВИ отказ (например заклинь книгу так, чтобы проход свипа не дошёл до конца) и сними снова. Ось закрывается диффом двух снимков и ответом «что оператор понял бы по этой дельте», а не прозой по чтению кода. ### 4.5 Предметные оси ревью по характеру работы (вправе заменить с аргументом) Пак читающий, поэтому из таблицы приёмок применимы две. **Код — исполнением:** батарея, живые пробы и СОБСТВЕННЫЕ посадки мутаций; сверка с индустриальным первоисточником прежде велосипеда (RFC для сессий и кук, практики именования метрик, объявленные `STACK_DECISIONS` §24); у ШВА — инвентарь ВСЕХ каналов другой стороны чтением ЧУЖОГО кода, не по памяти. **Деньги — пере-выводом чисел из СЫРЬЯ** двумя независимыми путями. Третью ось, если считаешь нужной, назови и аргументируй в эхо-протоколе. ## 5. Нормы - **Самопроверка ИСПОЛНЕНИЕМ — механизмом, а не перечтением.** Названный механизм: (а) **посадки мутаций** по норме зоны — портишь инвариант ровно так, как его называет комментарий или строка регистра, и смотришь, краснеет ли пин; непойманная мутация в несущем инварианте — находка, а не примечание; (б) прямые `SELECT` по стенду для денежных чисел; (в) **субагенты РАЗРЕШЕНЫ и рекомендуются** — четыре оси независимы и делятся между ними без потерь (иначе дефолт-запрет харнесса тихо победит). Артефакт самопроверки — таблица «**ось** → что посадил → какой пин обязан был упасть → упал ли». ⚠ **Пол — не меньше одной посадки НА КАЖДУЮ ось**: без него буква промта удовлетворяется одной посадкой на весь пак и четырьмя непрочитанными осями. - **Мутации — в КОПИИ дерева**, не в репозитории, и КАЖДОМУ агенту своя: `cp -a platform ~/tm-p8-review/<имя-агента>/platform`, путь выдаётся агенту ЯВНО в его задании — «работай в копии» без выданного пути есть приглашение к инциденту (D39.113). Каждому агенту — тот же запрет state-changing git ПЕРЕЧИСЛЕНИЕМ, что и тебе. В дереве живут незакоммиченные работы параллельных сессий; правка = их потеря (норма изоляции D39.113). - **Батарея.** `make check` с ТРЕМЯ условиями, а не двумя (испр. 22.08, `PD-374`): `TM_PLATFORM_TEST_DSN` · `TM_PLATFORM_TEST_ENGINE_BIN`+`_BOOK_TEMPLATE` · ДОСТИЖИМЫЙ пользовательский менеджер systemd (без него три теста `internal/runner` скипаются молча, и «скипов 0» на таком хосте недостижимо в принципе). **Скипов 0 — гейт**, а если условие на твоём хосте не выполнено — НАЗОВИ его вместе с числом скипов, не объявляй батарею зелёной; без переменных батарея МОЛЧА скипует ~200 тестов, и её зелень тогда не значит ничего. Рецепт стенда — `STACK_DECISIONS`, раздел «Стенд разработчика». - **Тесты и гейты не подгонять под зелень.** Правка или удаление теста ради прохода недопустимы; несогласие с тестом — вопрос через владельца, не правка. - **Заявление = команда.** Каждое число и каждая категорика отчёта — с командой, которой они получены; приёмка пере-ранит и сверит. **Перед отчётом сверь КАЖДЫЙ клейм с результатом инструмента ЭТОЙ сессии**, а не с памятью о том, что видел час назад — у пака, чей единственный продукт это утверждения, цена неверного клейма выше, чем у кода. - **Последний абзац отчёта — план или обещание? Сделай это сейчас**, а не пиши как намерение. - **Интервальная самоверификация** (четыре оси — длинная работа): раз в несколько часов субагент против ЯВНЫХ критериев «какая ось закрыта, какая объявлена закрытой без исполнения, что молча выпало», результат в отчёт. - **Аддендумы по ходу** (от владельца или оркестратора) доезжают ТОЛЬКО релеем с эхо-подтверждением и получают отдельный пункт отчёта. ⚠ Норма «дифф `^func Test` исполнением» к этому паку НЕ применяется сознательно: он не пишет тестов; если по ходу окажется, что пин всё-таки нужен — это вопрос, а не самодеятельность. - **Рамка находки (анти-оверфлаг):** флагай бьющее по КОРРЕКТНОСТИ, ДЕНЬГАМ, БЕЗОПАСНОСТИ или по объявленным зоной требованиям. Стилистика и «можно было бы красивее» — не находки. **Ноль находок по оси — законный результат**, если он получен исполнением; ревью, обязанное что-то найти, находит выдуманное. - **Улика обязательна.** `file:line` или команда с результатом. Эрудиция уликой не является: где это мнение — так и пиши «мнение». - **Записка-план** против §4 механически: ось → что смотрел → чем проверял → находки/чисто. Сдача = все четыре оси с диспозицией. - **Денег пак не тратит:** провайдерских вызовов здесь нет вовсе. Если тебе кажется, что нужен платный вызов — стоп и вопрос, не трать. - **Obstacle reporting** — обязательная секция: что НЕ удалось, что не проверено и почему. Пустой она не бывает. - **Канал вопросов:** непонятно, или промт против кода/доков → пинг через владельца, НЕ интерпретация. Право сказать «этого делать не надо» — с аргументом; если считаешь, что ось надо заменить или порядок другой, скажи это в эхо-протоколе. - **Плотность, не объём.** Без шаблонных оговорок и без пересказа того, что уже есть в доках. ## 6. Старт и сдача **Эхо-протокол:** первым деливераблом ≤10 строк — «скоуп / инварианты / не-делать», как ты их понял. До работы. **Артефакты для СЛЕДУЮЩЕЙ сессии — в репозиторий, в свою зону `platform/docs/`, не наружу и не в копию дерева:** скрипты и патчи, которыми находка воспроизводится, обязаны пережить твою сессию — иначе «воспроизведение» остаётся только текстом строки регистра. В строке регистра ссылайся на этот путь. **Сдача:** отчёт в `platform/docs/platform-PROGRESS.md` (записка-план по четырём осям · команды · таблица посадок · obstacle) + строки находок в `DEFECT_REGISTER.md` с воспроизведением. Дерево незакоммиченное, кода не тронуто. Без саги.