29 KiB
Промт: платформа, пак 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; файлы застейдженными не оставлять. ⚠ В дереве живёт НЕЗАКОММИЧЕННАЯ работа полигона (13 изменённых файлов и 6 новых под eval/) — не трогать и не «прибирать». Индекс на 22.08 чист (прежняя редакция писала «частично застейдженная» — испр.), но полигон-сессия жива и стейдж может появиться в любой момент.
⚠ РОВНО: кода не менять. Ни одной правки .go, ни «заодно поправил очевидное». Причина не бюрократическая: правка, внесённая тем же, кто нашёл, лишает следующий пак независимой проверки, а тебя — чистого измерения «сколько тут дефектов». Исключение одно — временные правки в КОПИИ дерева ради посадки мутаций (см. §5), в репозиторий они не возвращаются.
3. Читать (≤5)
platform/docs/DEFECT_REGISTER.md— открытые строки целиком; закрытые по твоим осям — выборочно, с оговоркой §1.platform/docs/STACK_DECISIONS.md— правила, которые не выводятся из одной функции: §22 (порядок блокировок), §33–36 (долг материализации,edit_wave, техникаxmin), раздел «Стенд разработчика» (сборка, ОБА гейта батареи, грабли).platform/docs/platform-PROGRESS.md— шапка (что построено и зачем) + пинг оркестратора №18 в хвосте: там фикс-лист приёмки P7, и его пункт 8 — это ты.platform/docs/ENGINEERING_STANDARDS.md§3 — норма посадок мутаций зоны. ⚠ Твой продукт — это ДЕСЯТКИ новыхfile:lineв регистре, а гейт якорей с 21.08 видитplatform/docsи умеет сверять по СОДЕРЖИМОМУ:python3 docs/scripts/counts.py --lint(от корня). Якорь может нести токен ожидания —`путь:12-14`=`подстрока`— и на строках, которые ты трогаешь, токен ТРЕБУЕТСЯ. Токен списывается из цели дословно, не по памяти: автор этого промта на этом попадался трижды за сутки.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, 00019.
Норма, обязательная к исполнению (канон приёмок). ⚠ Обязателен САМ ЗАМЕР, а не оформление уже найденного: сессия, ничего не нашедшая, чисел не производит — и норма «каждое число двумя путями» не связала бы ничего. Поэтому минимум: сведи на стенде баланс аккаунта, сумму леджера и открытые холды двумя путями ДО всякого поиска — расхождение здесь и есть первая находка. Дальше каждое число — из СЫРОГО леджера двумя независимыми путями (чтение кода/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 строк — «скоуп / инварианты / не-делать», как ты их понял. До работы.
Артефакты для СЛЕДУЮЩЕЙ сессии — на durable АБСОЛЮТНЫЙ путь ~/tm-handoff/p8-review/, не в scratchpad и не в копию дерева: скрипты и патчи, которыми находка воспроизводится, обязаны пережить твою сессию — иначе «воспроизведение» остаётся только текстом строки регистра. В строке регистра ссылайся на этот путь.
Сдача: отчёт в platform/docs/platform-PROGRESS.md (записка-план по четырём осям · команды · таблица посадок · obstacle) + строки находок в DEFECT_REGISTER.md с воспроизведением. Дерево незакоммиченное, кода не тронуто. Без саги.