# Промт: платформа, пак P12 «долги под ногами» — семь открытых major и висящие обязательства зоны ## 0. Какая проблема и что решит твой результат У зоны нет активной стройки, но регистр держит **7 открытых major** — и почти все они про ДЕНЬГИ или про ЛОЖЬ экрана: запись о правке банка теряется навсегда при обрыве клиента (`PD-425`), экран показывает «готово» по чужому прогону, пока живой тратит деньги невидимо (`PD-402`), смена состава волн на живой книге продаёт уже переведённые главы повторно (`PD-403`/`PD-404`), полоса стоит на нуле всю жизнь прогона (`PD-405`), намертво заблокированная расплата невидима и не выводится ничем, кроме Stop пользователя (`PD-424`). Плюс обязательство, висящее с 27.08: обход `--verify-bank` так и не снят, хотя движковая половина порядка D39.158 давно залендена. **Что решит твой результат:** деньги перестают теряться молча, экран перестаёт врать, зона входит в живой прогон (очередь №20 п.2–3) без известных мин. **Чего пак НЕ решает:** дверь выдачи `createExport`/`getExport` (пинг №20 в зонном журнале — она ждёт живого прогона) · `PD-410` (единица продажи — ОТДЕЛЬНЫМ паком, слово оркестратора 28.08, тело решения — D39.165 §1) · эскроу П-18 · перф-строки `PD-412`/`PD-413`. ## 1. Зона записи и git Пишешь только в `platform/`. **Не коммитишь**: дерево готовишь и передаёшь оркестратору. Канон git — в `CLAUDE.md`. ⚠ Одна операция выходит за код зоны и разрешена явно: `tmctl migrate` по СТЕНДОВЫМ книгам зоны свежесобранным бинарём (§3.7) — это операция над данными стенда, не правка `backend/`. ## 2. Карта чтения — ПЯТЬ позиций, дальше только по их ссылкам 1. `platform/docs/ENGINEERING_STANDARDS.md` — целиком; **DoD §3 — закон** (каждый его пункт исполняется буквально, отступление = пинг). 2. `platform/docs/STACK_DECISIONS.md` — разделы «Гейты батареи» (единственный носитель условий) · рецепт стенда · §35 (монотонность `edit_wave` — правило, которое этот пак пере-подписывает). 3. `platform/docs/DEFECT_REGISTER.md` — шапка/легенда + ТЕЛА адресуемых строк: `PD-425 402 403 404 405 424 418 411 369 420 431 432 427 203 367 422 213 423 380 44`. 4. `platform/docs/platform-PROGRESS.md` — пинг №20 (30.08) и **ПИНГ №21 (27.08, D39.158)** — носитель обязательства по обходу `--verify-bank` и СОСТАВА снятия (подавление флага при возобновлении + особый случай в спавне + персистентная колонка `bank_released`); вопросы sqlc-сессии. 5. `docs/architecture/05-decisions-log.md` — ТОЛЬКО тело `D39.165` (греп `^## D39.165`), §2 «эпоха» — источник решения владельца для §3.2; остальной журнал не читать. ## 3. Работа **Порядок при нехватке ресурса (ранжирование оркестратора):** first-class — §3.1 (деньги, теряемые молча) · §3.7 (обязательство, висящее с 27.08) · §3.3 (вход в живой прогон очереди №20); вторым эшелоном — §3.2 (вскроется конфликт с каноном — пинг, и пункт уезжает СЛЕДУЮЩИМ паком, не молчаливая отсрочка) · §3.6+§3.9 (дёшевы, тем же деревом); §3.4/§3.5/§3.8 — по остатку ресурса. Всё невзятое — диспозицией строкой в записке-плане §6; честная резка с доводом лучше молчаливой каши. ### 3.1 `PD-425` — обрыв клиента теряет запись о правке банка навсегда (деньги, делай РОВНО так по классу) Дверь коррекций пишет пост-verb факт (`RecordBankMove`) на контексте ЗАПРОСА (`internal/httpapi/bank.go:149`=`ApplyBankCorrections`): клиент закрыл вкладку до ответа — запись не легла, `bank_moved_at` NULL навсегда, второго писателя и свипа нет; дальше обычный прогон допускается без `--resnapshot` и умирает на снапшот-гарде движка ПОСЛЕ взятия холда. Средство «503, пере-шли документ» недостижимо — клиента уже нет. **Класс лечения зона назвала сама и уже дважды применила:** `WithTimeout(WithoutCancel(ctx), …)` — `platform/internal/httpapi/idempotency.go`, греп `WithoutCancel` (`settleCtx`; его комментарий — дословно про обрыв клиента) — ⚠ номера строк сняты: файл правишь ты же и `internal/books/books.go:205,215`=`writeCtx` (тело — `internal/books/parse.go` (греп `WithoutCancel`)). ⚠ Тело `PD-425` в регистре несёт БИТЫЙ якорь `pgstore/idempotency.go` (греп `IdempotentResponse` — там чтение ключа, detach нет) — пере-нацель его той же правкой регистра (гигиена §3.9). Делай тем же классом; деталь (таймаут, что именно накрывается) — реши сам и аргументируй. Пины свои: посадка «контекст отменён между verb и записью» обязана красить дефект и зеленеть с фиксом. ### 3.2 `PD-403`/`PD-404`/`PD-411`/`PD-405` — полоса и повторная продажа глав. РЕШЕНИЕ ВЛАДЕЛЬЦА ДАНО — исполняй, не пере-решай Строки стоят «лечение за решением владельца» — **решение дано 28.08, D39.165 §2, релей дословный:** смена формы конвейера (добавили/убрали редактора) — **СОБЫТИЕ КНИГИ, как пере-нарезка, и счёт легально пересчитывается** при пересечении границы; канон уже открыл дверь («never moves backwards WITHIN one `structure_version`; cutting the book again recomputes both numbers» — `docs/architecture/14-api-contract/openapi.yaml`, греп `never moves backwards` (⚠ номер снят: файл правишь ты же минором 0.9.0); ⚠ якорь D39.165 `1542-1545` дрейфанул — там сейчас RejectReason), нового исключения не изобретать. ⚠ **Носитель границы эпохи нотой НЕ решён — реши сам и аргументируй** в рамках: флаг монотонен (пин D39.153 §4б) · форму НЕ читать с последнего прогона · счёт пересчитывается только на СОБЫТИИ. Цена обеих дорог: пересчёт БЕЗ движения `structure_version` нарушает букву канона на направлении «вниз»; движение `structure_version` инвалидирует курсоры/идентификаторы пар/окна термов (`openapi.yaml:1233-1236`=`moves when the book is cut`). Если выбранная форма требует правки ФРАЗЫ канона (второе событие пересчёта рядом с пере-нарезкой) — это контрактный минор через ратификацию оркестратора: пинг ДО стройки, прецеденты миноров 0.7.0/0.8.0 есть. При этом: - ⚠ **Монотонность флага (D39.153 §4б) НЕ отменяется** — пин стоит, правка пина под зелень запрещена (D39.121). Меняется другое: пожизненный счёт книги перестаёт выводиться из ЖИВОГО флага. - ⚠ Ложный док-комментарий `recordWaveShape` — `internal/pgstore/sink.go`, греп `Monotone in one direction` (⚠ номера строк сняты 30.08: файл правит эта же сессия; редакция D39.165 писала `405-408`, аудит нашёл `406-409` — дрейф) — переписать/снести целиком вместе с правкой поведения, не механически диапазон строк. - ⛔ **Заново НЕ предлагать** четыре отвергнутые формы: счёт всегда по редакторской волне · читать форму с последнего прогона · немонотонный флаг · «какая фаза считается» как вопрос клиенту. - **§35 `STACK_DECISIONS.md` пере-подписать** той же работой — это правка ЗАПИСАННОГО правила зоны, не только кода; прежняя формулировка остаётся в истории с датой и причиной. - **`PD-411`** — мёртвые `runs.draft_done/draft_total/edit_done/edit_total` (два писателя, ноль читателей): запрет сноса был пер-паковым (P9); теперь снос миграцией — естественная часть этой же правки. Реши сам, этим ли паком; если нет — диспозиция строкой. - **`PD-405`** — прогон над книгой без материализованного дерева читает 0/total всю жизнь. Зона назвала два лечения: отказ старта до дерева ИЛИ полоса из progress-событий. Выбери и аргументируй; ложный комментарий `sink.go:219` снести. ### 3.3 `PD-402`, read-половина — экран не смеет показывать «готово» по чужому прогону (делай РОВНО так) Write-гард уже закрыт (F12, `ErrNotResumable`, пин `TestAnOlderRunCannotBeResumedOverANewerOne`); осталась read-половина: `lastRun` берёт `order by started_at desc` — живой прогон обязан иметь приоритет над финишировавшим в карточке/полосе. ⚠ У `lastRun` ДЕСЯТЬ мест склейки на 30.08 (десятое — `pgstore/runs.go:132`=`lastRun`, добавлено 28.08 ПОСЛЕ счёта «девять» из PD-44; пере-сочти сам грепом паттерна склейки, не голым словом — голое ловит комментарии) и он в ⛔-зоне sqlc — правка судится СВОИМИ тестами по каждому месту, не «зеленью батареи». ### 3.4 `PD-424` (+`PD-418`) — намертво заблокированная расплата обязана быть видимой и выводимой Живой прогон с блокированной расплатой: `reopen` → вердикт `deferred` → `return nil` + `ClearRunDeferral`, счётчик не растёт никогда, холд заморожен, не видит ни одна поверхность `PD-385`, `run abandon` не берёт. **Приор зоны (опровергается аргументом):** `deferred` ⇒ `established=false`, блокированный расчёт = неудача владеющей фазы. `PD-418` (settling живого прогона без ручки) — тот же узел: реши, закрывается ли он той же правкой, и подпиши диспозицию. ### 3.5 `PD-369` (=П-21) + `PD-420` — легитимный запрос не имеет права получать 500 Конкуренция на одном ключе идемпотентности: `claimRounds=3`, 8 гонщиков отбирают гонку трижды → 500. Зона назвала две формы: граница по ВРЕМЕНИ либо явный `ErrKeyInFlight` → контрактный ответ. **Слово оркестратора: 500 на легитимном запросе недопустим; между двумя формами выбери сам и аргументируй** (учти словарь кодов контракта — новый код = вопрос, маппинг существующих — нет). `PD-420` — тот же узел (её диагноз НЕ установлен): порядок — СНАЧАЛА фикс `PD-369` (форма `ErrKeyInFlight` зеленит тест по построению — он этот error уже принимает), ПОТОМ пере-замер серией (≥80 итераций + параллельные батареи); флейк пережил фикс — тогда изоляция/скип с диагнозом строкой в регистр; скип ДО фикса — снятие пина, не вариант (D39.121). Вердикты посадок — по ДЕЛЬТЕ и топичности (DoD §3), красная чистая копия не маскирует дельту. ### 3.6 `PD-431` + `PD-432` — честность носителей (делай РОВНО так) - `PD-431`: `Touch` (`internal/pgstore/sessions.go:49`=`func (s *Store) Touch`) выбрасывает `RowsAffected` — апдейт в ноль строк неотличим от успеха, скольжение окна бездействия не проверяет ни один живой тест. **Слово оркестратора: поведение НЕ менять** (ноль строк — законная гонка с отзывом); лечение — честность проверки: запинить скольжение окна СКВОЗНЫМ тестом через `auth.Authenticator` с живым `*pgstore.Store` (сегодня их не связывает ни один тест). Если по ходу увидишь, что и поведение требует слова, — пинг, не правка. - `PD-432`: рецепт гейтов батареи в `STACK_DECISIONS.md` не говорит, что движковый бинарь стенда надо ПЕРЕСОБИРАТЬ из текущего `backend/` — допиши строку рецепта (свой док зоны; протухший `tmctl` даёт три красных теста «field … not found», читающиеся как дефект зоны). **Той же док-правкой:** команда-проверка четвёртого условия батареи (`cut -d: -f3 /proc/self/cgroup` ≠ `/init.scope`) УЖЕ стоит в «Гейтах батареи» и ОПРОВЕРГНУТА второй точкой `PD-423` (ложный отрицательный: тест зелен 5/5 при `/init.scope`) — сними её или пере-подпиши как опровергнутую со ссылкой на `PD-423`; прежняя формулировка остаётся в истории с датой и причиной. Замену-кандидата (`cgroup.subtree_control` целевого среза) можно назвать только КАНДИДАТОМ — диагноз `PD-423` не установлен, строке дай диспозицию, не закрытие. ### 3.7 Снятие обхода `--verify-bank` — довести порядок D39.158 до конца (делай РОВНО так) Порядок односторонний и записан: движок первым (залендён 27.08) → `tmctl migrate` по КАЖДОЙ книге → только потом платформа снимает обход. Сделай: пересобери `tmctl` из ТЕКУЩЕГО `backend/` → `tmctl migrate` по каждой стендовой книге зоны → сними обход в коде → живой пробой на стенде: стоп банка доезжает флажком движка, а не обходом. ⚠ **Той же работой закрывается строка бэклога 240 (находка Д3 ревью P9, двое суток без носителя): оплаченный стоп подписи исчезает МОЛЧА** — рестарт проходит границу банка насквозь, а гард `LiftBankStop` писан против движка ДО памяти v16 и этот путь не держит. Реши ОДНОЙ работой: снять гард вместе с обходом ЛИБО доказать живой пробой, что память `bank_stop_presented` (схема v16) делает его недостижимым; молчаливо оставить как есть — нельзя. **Состав снятия — по пингу №21, он ШИРЕ спавна:** (а) особый случай в спавне — `platform/internal/runs/spawn.go`, греп `BankReleased` (⚠ номера строк здесь НЕ ставим: файл правит эта же сессия) — снять РОВНО; (б) писатель `LiftBankStop` (греп `LiftBankStop` по `internal/runs/reconcile.go` и `internal/pgstore/runs.go` — номера строк двигаются под твоей же правкой) и (в) колонка `bank_released` с читателями полосы (греп `bank_released` по `internal/pgstore/readmodel.go` — номера строк двигаются под твоей же правкой) — реши сам, этим ли паком: снос = НОВАЯ миграция (00016 не править) + пере-вывод сегмента полосы; (б)/(в) сцеплены — снять писателя, оставив колонку, значит заморозить `bank_released=false` и врать полосой (ось §5); не этим паком — диспозиция строкой, как у `PD-411`. ⚠ **Снимаемое поведение ЗАПИНЕНО:** `TestAResumedRunIsSpawnedWithoutTheSigningStop` (греп `TestAResumedRunIsSpawnedWithoutTheSigningStop` по `platform/internal/runs/reconcile_test.go`; его же комментарий — «Mutation caught: passing l.VerifyBank unconditionally», т.е. ровно заказанная правка). **Пере-подпись этого пина заказана этим промтом** — как в §3.8: заказанная смена контракта, не подгонка под зелень (D39.121); пере-подписанные пины перечисли поимённо. Той же работой уходят обоснования сброса флага из фикс-раунда 28.08 (строка F10, `platform-PROGRESS.md:1467`): комментарий `spawn.go:159-169` («the owner's model») и док-коммент теста — оба описывают поведение, которое пинг №21 объявил лишним. Если найдёшь причину, по которой снимать рано, — пинг с уликой, не молчаливая отсрочка. **Стенд УЖЕ СТОИТ** — второй по рецепту НЕ заводить: `~/.local/share/tmstand` (стендовая книга одна — `books/bk_WIUSGBQTIGWE4ZBY`; бинари в `bin/`, `tmctl` — `~/.local/bin/tmctl`); `W=~/tmstand-work` в рецепте — шаблон. Живой `tmplatformd` — демон ЗАКРЫТОЙ сессии оркестратора №19: убей по PID (⚠ не `pkill -f` — инцидент P11) и подними свой свежесобранный. Для живого пробоя $0-пары подними СВОЮ заглушку на `127.0.0.1:11434` (образец — `platform/internal/runner/translate_resnapshot_live_test.go:41`=`Listen`; ollama на хосте нет) и ⚠ гаси её на время `make check`: тест второго гейта при занятом порту скипается, а требование — скипов 0. ### 3.8 `PD-367` — интейк не принимает манифест, который свой же материализатор отверг бы (решение оркестратора) Сегодня `{ChaptersTotal:120, пустой список}` заводит книгу, по которой можно СТАРТОВАТЬ И ОПЛАТИТЬ прогон. **Решено: пол самосогласованности — симметричный, интейк отказывает** (закон шва: незнакомое/ несогласованное → не-деструктивно и громко). Это ПРАВКА ЗАПИНЕННОГО контракта интейка — расширение заказано этим промтом, правки пинов перечисли поимённо в отчёте (это не «подгонка под зелень», это заказанная смена контракта; сомнение — пинг). Батарея интейка ездит на документах без списка глав — фикстуры расширь, не обходи. Смежную `PD-213` (незнакомая версия манифеста ведёт к УДАЛЕНИЮ файла) реши тем же классом «незнакомое → не-деструктивно» или подпиши диспозицию строкой. ### 3.9 Гигиена регистра (дёшево, тем же деревом) Закрыть `PD-427` (комментарий уже исправлен 29.08 — акт закрытия) и `PD-203` («предложено закрыть» ещё P8-REVIEW; проверь предикат и закрой либо оспорь) · перенести `PD-380` и `PD-44` (обе fixed) из-под чужих заголовков «Открытые» · завести строку на НЕПОКРЫТУЮ гоночную ветку `errIdentityRace` (`pgstore/identity.go:99-113`=`race` — ретрай при `on conflict … do nothing`, гонка двух первых логинов одной новой identity; ни один тест её не касается, все тестовые вызовы последовательные) с диспозицией: запинить конкурентным тестом ЛИБО доказать недостижимость; ⛔ снос — только с доказательством: ветка обслуживает ЛЕГИТИМНУЮ гонку логина, её снос = 500 на легитимном пути (класс §3.5) · **пере-диспозиционировать `PD-403` и `PD-410`**: обе несут «лечение за решением владельца/оркестратора», хотя D39.165 §2/§1 дали направление ещё 28.08 — регистр отстаёт от D-лога, и следующий читатель понесёт владельцу уже отвеченное · `PD-422` НЕ трогать (гейчена проводкой `--max-units`, гейт оркестратора) — просто не забудь её в грепе по своим путям. ### 3.10 Границы - ⛔ `backend/` не трогать (исключение — операция §3.7); ⛔ дверь выдачи не строить; ⛔ эскроу не строить; ⛔ `PD-410` не лечить (отдельный пак). - ⛔ `observe.go` под sqlc не конвертировать, схему River в sqlc-конфиг не добавлять; склейки/read-модель — ⛔-зона sqlc; выпущенные миграции не править (append-only; новые — можно). - Новая зависимость = строка в `STACK_DECISIONS.md` с датой и «зачем», ратифицирует оркестратор; Redis/ORM/ OTel — записанные «нет». - Деньги: суммы не на провод/UI/INFO/метрики (D39.84); леджер append-only держит код. ## 4. Самопроверка ИСПОЛНЕНИЕМ (без неё работа не принята) — DoD §3 стандартов зоны, поимённо - **`make check` целиком** при ТРЁХ гейтах + условии хоста (носитель условий — `STACK_DECISIONS.md` «Гейты батареи»); скипы считать КОМАНДОЙ и называть вслух; зелень = полный список пакетов + 0 FAIL. ⚠ Статус четвёртого условия НЕ выводить из команды `/proc/self/cgroup` — она уже опровергнута второй точкой `PD-423` (ложный отрицательный) и пере-подписывается в рецепте по §3.6; суди по самому тесту (`TestARunIsBoundedByItsOwnCgroup` зелен при 0 скипов); красный — сначала проверь среду (`cgroup.subtree_control` среза, рецепт в `STACK_DECISIONS.md`), потом ищи дефект. - **Стенд:** пересобрать ВСЕ ТРИ бинаря, включая `tmctl` из текущего `backend/`; демона убивать ПО PID (⚠ не `pkill -f` — инцидент P11); `pipeline-zero` для второго гейта; `curl --noproxy '*'`; правил миграцию — пересоздай базу. - **Мутационные посадки — в КОПИИ дерева ВМЕСТЕ с каноном** (`cp -a --parents platform docs/architecture/14-api-contract <куда>/`); вердикт по ДЕЛЬТЕ против чистой базы ТОЙ ЖЕ копии и по ТОПИЧНОСТИ упавшего теста, не по цвету. Каждое заявленное свойство несущего пути — запинено тестом, ловящим мутацию; в отчёте назови, какой тест что пинит. - **Греп ОТКРЫТЫХ строк регистра по СВОИМ файлам ПОЛНЫМИ путями** — каждое совпадение получает диспозицию; якорь, убитый твоим переездом, чинишь ты. - Находки — строками в регистр ТЕМ ЖЕ деревом; правки чужих до-паковых тестов — поимённо. - ⛔ Тесты/голдены/гейты не подгонять под зелень (D39.121); несогласие — пинг. - **Дифф `^func Test` — исполнением**; интервальная самоверификация субагентом в середине пака. - **Разрешаю субагентов**; модель задавай явно; трудный вопрос — старшей модели. - ⚠ **Адверсариальный проход по СВОЕЙ готовой работе перед сдачей.** Направления: деньги, теряемые между verb и записью; экран, который врёт при гонке живого и финишировавшего; пересчёт счёта, продающий купленное; 500 на легитимном пути; зелёный тест, не проверяющий ничего (класс D39.171). Пока идёт проход — дерево не двигается: морозь или называй агенту коммит. ## 5. Оси ревью **Деньги** (ни одна запись о деньгах не теряется молча; сверка леджера двумя путями) · **честность экрана** (полоса/карточка не врут ни при гонке, ни при смене формы) · **контрактное поведение** (интейк/идемпотентность/окно сессии отвечают то, что обещано, и это запинено). Вправе заменить ось с аргументом. ## 6. Записка-план и комплектность До кода — записка-план в `platform/docs/platform-PROGRESS.md`: что берёшь, что не берёшь, чем докажешь; числа — свои, командами, не унаследованные из промта. Перед сдачей — механическая сверка состава против §3 таблицей: пункт → сделано/нет → чем доказано. Пропуск подписывается пропуском. **Последний абзац отчёта оказался планом или обещанием? Сделай сейчас.** ## 7. Канал вопросов и право отказаться Конфликт промта с кодом/доками/регистром — **пинг через владельца, не интерпретация**. Есть право сказать «этого делать не надо» с аргументом (по любому пункту §3 — состав большой, честная резка с доводом лучше молчаливой каши). ⚠ Правки задания по ходу работы доезжают ТОЛЬКО релеем через владельца — отдельным его сообщением. Что не пришло релеем — не заказ. Полученную правку эхо-подтверди и вынеси отдельным пунктом отчёта. ## 8. Заявление = команда Любое число и категорика — с командой получения; приёмка пере-ранит, клейм без пере-прогона = «со слов сессии». Утверждение без `file:line`/замера помечай словом «мнение». ## 9. Эхо-протокол старта ДО первой правки — ≤10 строк: скоуп / инварианты / чего не делаешь. Отправь первым действием по адресу оркестратора из `/tmp/textmachine-channel` (механизм — `CLAUDE.md`; впиши туда СВОЙ блок первым делом). **Канала нет ⇒ НЕ искать**: вопрос секцией в отчёт, работа продолжается. ## 10. Obstacle reporting Обязательная секция: что НЕ удалось и что осталось непроверенным. Пустой не бывает. Три исхода: «подтверждено» / «опровергнуто» / «не проверено» — третье не сваливать во второе. ## 11. Журнал Записка-план, итоги и вопросы — ЗОННЫЙ `platform/docs/platform-PROGRESS.md` (в `docs/PROGRESS.md` платформа не пишет).