# Промт: платформа, пак P11 — отзыв сессии перестаёт быть словом, застрявшие деньги перестают быть невидимыми > **Выдан оркестратором №19, 28.08.2026.** Пак идёт ПАРАЛЛЕЛЬНО бэкендовому. > ⚠ **ФАЙЛЫ вы не делите** (модули отдельные, платформа кода движка не компилирует — проверено), > **но ШОВ общий и ведётся руками:** таблица кодов выхода · argv литералами · аллоулист `status --json`. > Бэкендовый пак трогает первые две. Увидишь расхождение — ПИНГ, батарея его не поймает по построению. > Источник состава — читающий пак **P8-REVIEW** (D39.159): 24 строки регистра, открытых **23**. > **Этот пак берёт СЕМЬ**, и почему именно их — §3.5. ## §0. Какая проблема и что решит твой результат **1. «Выйти везде» не выключает уже открытый канал — `PD-379`, единственный открытый `major` уязвимости.** Аутентификация происходит РОВНО ОДИН РАЗ, на входе в `guard`; дальше `streamEvents` уходит в `pump`, и цикл до конца соединения строку сессии не смотрит НИ РАЗУ. Значит `logout`, `logout-all`, `tmplatformctl revoke` и оба потолка сессии (idle и абсолютный) открытый `GET /v0/books/{bookId}/events` не прекращают. ⚠ **Это пере-проверено ТРИЖДЫ независимо** (финдер · рефутер в отдельной копии · координатор пака): `revoke --user` отчитался «revoked 4 sessions», новый запрос той же кукой дал `401`, **а тот же поток через 9 секунд ПОСЛЕ отзыва отдал настоящий кадр данных**. На демоне с пятисекундным idle и десятисекундным абсолютным потолком поток жил ещё сорок секунд. ⚠ **Бьёт по ОБЪЯВЛЕННОЙ норме, а не по вкусу:** ASVS 5.0 7.4.1 — требование УРОВНЯ 1 при объявленном зоной L2. И `STACK_DECISIONS` §13 отказывается от лимита одновременных сессий **именно в обмен на мгновенный отзыв** — то есть цена уже заплачена, а товар не получен. **2. Застрявшие деньги невидимы ТАМ, КУДА ПОСЫЛАЕТ ЛОГ — `PD-385`, второй `major`.** ⚠ **Абсолюта «невидим вообще» тут НЕТ** — он опровергнут рефутером прошлого пака, и первая редакция этого промта его по ошибке повторяла: холд ВИДЕН через `tmplatformctl balance --user`, гейдж старейшего открытого холда растёт, `tmplatformctl books` пишет «WHY NOT: unsettled hold». **Слепы ТРИ поверхности ПОРОГА, и терминальной ручки нет.** Живая проба на состоянии, выращенном штатными путями: `runs --stalled` отвечает «no run is failing to reconcile», `runs` — «no run is live», `run abandon` — «is not a live run», гейдж 0 — **при этом в базе `settled_at` NULL, пять неудач реконсиляции и открытая резервация на 90 000 микро-USD.** **Что решит результат:** отзыв сессии станет действием, а не словом; а деньги, застрявшие в расчёте, станут видны тому, кого лог за ними и посылает. ## §1. Зона записи и git **Твоя зона — `platform/`.** Итоги, находки, вопросы — зонный журнал `platform/docs/platform-PROGRESS.md`. - **Ты НЕ коммитишь.** Лендит оркестратор после адверсариальной приёмки. - ⚠ **`backend/` НЕ ТРОГАТЬ.** Там параллельно работает бэкенд-пак «деньги». Нужна правка там — ПИНГ. - ⚠ **`docs/` — не твоя зона.** Нужна правка канона контракта — опиши ПРЕДЛОЖЕНИЕМ в своём журнале, правлю я: канон и константа версии обязаны сойтись в ОДНОМ коммите, а коммичу я. - ⚠ **Стендовый `tmctl` собирай из ЗАФИКСИРОВАННОЙ копии** `backend/`, если он понадобится: рабочее дерево сейчас правит параллельная сессия, и бинарь из него мерит непонятно что. Без него живые тесты просто уходят в SKIP — это нормально и батарея остаётся зелёной. - В дереве незакоммиченная работа полигона (20 позиций) — не касаться. ## §2. Карта чтения — ≤5 позиций, ЗАКОН 1. **`platform/docs/DEFECT_REGISTER.md`**, строки `PD-379` · `PD-385` · `PD-384` · `PD-391` · `PD-394` · `PD-397` — **тела читать целиком**, там траектории и живые пробы предыдущего пака. 2. **`platform/internal/httpapi/stream.go`** целиком + `guard`/`Require` в `auth` — предмет `PD-379`. 3. **`platform/internal/runs/reconcile.go`** — `settlePhase`, `deferItem`, `StalledAfter`; и **`platform/internal/pgstore/runs.go`** — `StalledRuns` (`:406`) и `AbandonRun` (`:452`). ⚠ `AbandonRun` живёт в `pgstore`, а не в `internal/runs` — первая редакция приписала его не туда. 4. **`platform/docs/STACK_DECISIONS.md` §13** — там объявлен размен «нет лимита сессий В ОБМЕН на мгновенный отзыв», и §-норма уровня ASVS. 5. **`docs/architecture/05-decisions-log.md`, тело D39.159** (греп `^## D39.159`) — акт приёмки читающего пака, откуда взялись эти строки. ## §3. Состав пака — ШЕСТЬ строк, две группы ### §3.1. `PD-379` — отзыв обязан гасить открытый поток. ЗАКАЗ, форма свободна **Заказано:** после `logout` / `logout-all` / `revoke`, а равно по истечении **АБСОЛЮТНОГО** потолка сессии, уже открытый поток событий прекращается. ⛔ **Idle-потолок из заказа ИСКЛЮЧЁН, и это поправка (нашёл опровергатель):** окно бездействия скользит только на ЗАПРОСЕ (`auth/middleware.go:63-66`), а SSE — ОДИН запрос на всю жизнь соединения. Значит поток по определению никогда не трогает свою сессию, и гашение по idle убивало бы поток под АКТИВНЫМ пользователем — это регресс, а не починка. **Свободен и обязан обосновать — ГДЕ проверять.** ⚠ **И я зря пугал ценой:** `pump` уже зовёт `ReadStream` на КАЖДОЙ итерации (`stream.go:169`; комментарий `:29-33` сам считает «два индексированных запроса в секунду на соединение»), так что проверка сессии кладётся в тот же тик и лишнего запроса НЕ создаёт. **Значит «окно» заводить не обязательно** — первая редакция гнала тебя к нему страхом, которого нет. Если окно всё же остаётся — назови его ЧИСЛОМ. ⛔ **Сигнала «тебя выгнали» в протоколе НЕТ, и это придётся решать.** Заголовок ответа уходит ДО `pump` (`stream.go:84-85`), поэтому кодом ответа уже не скажешь; терминальных событий ровно два — `end` («книга закончена») и `resync_required` («пере-синхронизируйся»), и оба скажут клиенту неправду. **Новое имя события — правка КАНОНА, которую §1 тебе запрещает.** Поэтому: форму назови ПРЕДЛОЖЕНИЕМ в журнале, я впишу в канон и подниму версию; в коде реализуй так, чтобы имя менялось одной строкой. ⚠ И не сломай догон: `hello` несёт позицию, гашение обязано быть отличимо от обрыва сети. ### §3.2. `PD-385` — застрявший расчёт обязан быть виден. ЗАКАЗ **Заказано:** прогон с недоведённым расчётом виден операторской поверхности, на которую его посылает лог-строка, и с ним можно что-то сделать. ⚠ **Рефутер предыдущего пака ОПРОВЕРГ заголовочный абсолют «невидим» — тело строки это несёт.** Прочти тело целиком и не чини то, что уже видно: заказ про КОНКРЕТНЫЙ слепой угол, а не про «сделать всё видимым». ### §3.3. Денежная группа — четыре строки, все про ОДНО `PD-384` (расчёт, упавший ДЁШЕВО, не считается нигде) · `PD-391` (`run abandon` не возвращает холд «на ближайшем свипе») · `PD-394` (гард отрицательного расхода в `Settle` не запинен) · `PD-397` (два самых сильных денежных инварианта объявлены, но не запинены). **Заказано:** каждая закрыта либо ПИНОМ, либо строкой с причиной, почему не закрывается. ⚠ **Три из четырёх — про ПИНЫ, а не про код.** Это не «мелочь для галочки»: `PD-397` прямо говорит, что сильнейшие денежные инварианты держатся на слове. Пин, посаженный мутацией, и есть предмет. ### §3.4. Свободная форма и право пере-формулировать Тела строк писаны неделю назад читающим паком. **Дерево с тех пор двигалось трижды** (D39.162, D39.166 и фикс-раунды). Если строка описывает состояние, которого уже нет, — **это находка, скажи её, а не чини по букве.** Сегодня такое случалось: строка регистра держалась на основании, снесённом чужим паком. ### §3.5. Чего в паке НЕТ — пропуски ПОДПИСАНЫ ⚠ **Счёт первой редакции был неверен (поправка опровергателя): строк пака 24, закрыта только `PD-398`, значит открытых ДВАДЦАТЬ ТРИ.** Беру СЕМЬ. - **Взято:** обе `major` (уязвимость и невидимые деньги) плюс денежная группа, которая с `PD-385` составляет ОДИН предмет — «деньги, застрявшие или не посчитанные». - ⚠ **`PD-376` ДОБАВЛЕНА** — первая редакция не назвала её НИ ВО ВЗЯТЫХ, НИ В ПРОПУСКАХ, а это нарушение нормы «пропуски подписаны», и на самом дешёвом пункте: строка ДЕНЕЖНАЯ (`PD-159` стоит `fixed`, но её пин доказывает свойство СЛАБЕЕ, чем читается — мутация проходит батарею), а готовый пин уже написан ФАЙЛОМ в `platform/docs/p8-review/axis1-money/`. Возьми и проверь посадкой, что он ловит именно ту мутацию. - **НЕ взято и почему:** группа телеметрии (`PD-389`, `PD-390`, `PD-392`, `PD-393`) — отдельный предмет, ей нужен свой пак и своя ось ревью · группа сессий и кук (`PD-380`…`PD-383`) — родня `PD-379`, но **сознательно оставлена**: сначала закрыть уязвимость, потом чистить её окрестность, иначе пак распухнет вокруг самого срочного · `PD-375`, `PD-377`, `PD-378`, `PD-386`…`PD-388`, `PD-395`, `PD-396` — россыпь `minor`/`info`, идёт следующим паком. ⚠ `PD-398` первая редакция включила в этот список ОШИБОЧНО — она уже `fixed` (приёмка 27.08). - ⚠ **Увидишь по дороге, что невзятая строка чинится ОДНОЙ строкой внутри твоего же диффа — бери и скажи.** Запрет тут не ради чистоты, а ради размера. ## §4. Самопроверка ИСПОЛНЕНИЕМ — «перечитал сам» её не удовлетворяет 1. **Батарея зоны** под `-race` С ЖИВЫМ Postgres, линтер 0 issues; числа — с командами. ⚠ **`TM_PLATFORM_TEST_DSN` ставить ОБЯЗАТЕЛЬНО:** без него значительная часть батареи уходит в SKIP, а зелень рапортуется всё равно. **Число пропусков посчитай КОМАНДОЙ** и приведи с DSN и без. 2. ⛔ **`PD-379` доказывается ЖИВЬЁМ и ДВУМЯ РАЗДЕЛЬНЫМИ сценариями — первая редакция склеивала их в один, и склейка делала регресс зелёным** (нашёл опровергатель): реализация «таймер до абсолютного потолка» гасит поток по потолку, сценарий зелен, а ОТЗЫВ не починен. **(а) ДЛИННЫЕ потолки + `revoke`** → доказывает отзыв. **(б) короткие потолки БЕЗ отзыва** → доказывает потолок. ⚠ Юнит на «функция проверки вызвана» — вырожденный: сегодня она вызвана один раз на входе, и это ровно дефект. 3. **`PD-385` доказывается СОСТОЯНИЕМ, выращенным штатными путями** (интейк → HTTP-старт → отказ спавна → `abandon`), как его и нашли, а не подложенной строкой в базе. 4. **Свои посадки мутаций:** минимум по одной на пункт; вердикт — по ДЕЛЬТЕ и ТОПИЧНОСТИ упавшего теста, не по цвету батареи. 5. **Адверсариальный проход по своей готовой работе — заказан; глубину и веер выбираешь сама.** ⚠ Куда смотреть в ЭТОМ паке: что видит тот, у кого отозвали доступ · может ли починка вернуть холд дважды или чужой · поток под нагрузкой. Дефекты, которые внёс сам пак, называй первыми — приёмке они не видны. ⚠ **Fable 5** — обычно хватает одного-двух агентов, но это РЕКОМЕНДАЦИЯ, не потолок: веер под предмет выбирает сессия. Модель задавай ЯВНО и знай, сколько их работает. ## §5. Оси ревью — 1–3, вправе заменить с аргументом 1. **Ось «что видит атакующий»** — не оператор и не тест: тот, у кого отозвали доступ. 2. **Ось денег:** может ли починка `PD-385`/`PD-391` дважды вернуть один холд или вернуть чужой. 3. **Ось «поток под нагрузкой»:** пере-проверка сессии на каждом кадре — это запрос к стору на каждый тик; не превращается ли гашение в новую стоимость. ## §6. Записка-план, комплектность **До правок** — записка-план в зонном журнале. **В конце** — таблица комплектности против §3: строка → что сделано → каким ИСПОЛНЕНИЕМ подтверждено. Слово вместо команды = пункт НЕ сделан. ## §7. Эхо-протокол старта ДО работы — ≤10 строк: **скоуп · инварианты · не-делать**. Первое действие — вписать свой блок в файл канала (§10), сразу второе — послать мне эхо. ## §8. Obstacle — обязательная секция **«Что НЕ удалось и что НЕ проверено»** отдельной секцией, не россыпью. ## §9. Канал вопросов и твоё право отказаться Конфликт промта с кодом или доками — **пинг, не интерпретация в свою пользу**. ⚠ **У тебя есть право сказать «этого делать не надо» — с аргументом.** За последние сутки исполнители опровергли ЧЕТЫРЕ моих заказа и все четыре раза были правы. Отказ с разбором дороже послушного исполнения. ⚠ **Тесты и гейты не подгонять под зелень** (D39.121). ## §10. Как со мной связаться **Адрес — в файле `/tmp/textmachine-channel`.** Впиши свой блок первым делом, чужие не трогай. ⚠ **Канала нет ⇒ НЕ искать:** вопрос секцией в отчёт, работа продолжается.