textmachine/docs/PLATFORM_P11_SESSION_PROMPT.md

187 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Промт: платформа, пак 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. Оси ревью — 13, вправе заменить с аргументом
1. **Ось «что видит атакующий»** — не оператор и не тест: тот, у кого отозвали доступ.
2. **Ось денег:** может ли починка `PD-385`/`PD-391` дважды вернуть один холд или вернуть чужой.
3. **Ось «поток под нагрузкой»:** пере-проверка сессии на каждом кадре — это запрос к стору на каждый
тик; не превращается ли гашение в новую стоимость.
## §6. Записка-план, комплектность
**До правок** — записка-план в зонном журнале. **В конце** — таблица комплектности против §3: строка →
что сделано → каким ИСПОЛНЕНИЕМ подтверждено. Слово вместо команды = пункт НЕ сделан.
## §7. Эхо-протокол старта
ДО работы — ≤10 строк: **скоуп · инварианты · не-делать**. Первое действие — вписать свой блок в файл
канала (§10), сразу второе — послать мне эхо.
## §8. Obstacle — обязательная секция
**«Что НЕ удалось и что НЕ проверено»** отдельной секцией, не россыпью.
## §9. Канал вопросов и твоё право отказаться
Конфликт промта с кодом или доками — **пинг, не интерпретация в свою пользу**.
**У тебя есть право сказать «этого делать не надо» — с аргументом.** За последние сутки исполнители
опровергли ЧЕТЫРЕ моих заказа и все четыре раза были правы. Отказ с разбором дороже послушного
исполнения.
**Тесты и гейты не подгонять под зелень** (D39.121).
## §10. Как со мной связаться
**Адрес — в файле `/tmp/textmachine-channel`.** Впиши свой блок первым делом, чужие не трогай.
**Канала нет ⇒ НЕ искать:** вопрос секцией в отчёт, работа продолжается.