textmachine/platform/docs/PLATFORM_P5_SESSION_PROMPT.md

141 lines
16 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.

# Промт: платформа-сессия P5 — `POST /books` (П-9) + стоп/резюм (PD-140) + наблюдаемость (П-11)
Ты — платформа-сессия TextMachine. Зона записи — **только `platform/`**; сессия НЕ коммитит —
дерево готовит и передаёт на лендинг оркестратору. Онбординг по `CLAUDE.md`; итоги, вопросы и
предложения на ратификацию — ТОЛЬКО зонный журнал `platform/docs/platform-PROGRESS.md`
`docs/PROGRESS.md` платформа не пишет). В дереве живут незакоммиченные файлы ЧУЖИХ зон —
полигон (`eval/*`, ЖИВОЙ платный прогон: ничего там не запускать) и, возможно, фронт
(`frontend/*`) и бэкенд; начни с `git status`, чужое не трогать. Чужой код ЧИТАТЬ для сверки
можно и нужно (норма D39.115 п.3), писать — нельзя. LLM-провайдеров пак не зовёт, $0.
> **Онбординг-блок (проблема).** Раннер P4 построен и принят (D39.123): транзиентные
> systemd-юниты, очередь River с холдом-до-спавна, реконсилятор, тейлер, пять ручек `/v0`.
> Но книги заводятся только дев-инструментом `tmplatformctl book add`, а единственная ручка
> контракта, которую P4 сознательно не взял, — `POST /books` (граница промта, П-9 зонного
> бэклога): это не «ещё один хендлер», а хранилище файла, статусы `uploading/parsing/rejected`
> (в схеме контракта есть, ни одним писателем не заполняются), каталог проекта движка и
> пер-маршрутный потолок тела (PD-72 закрывается ВМЕСТЕ с ручкой, не раньше). Вторая дыра:
> ручек стопа/резюма нет (PD-140, `Service.Stop` не подключён) — человек не может остановить
> прогон. Третья: наблюдаемости нет вовсе (П-11: ни метрик, ни трейсинга; глубина очереди и
> возраст холдов читаются только SQL-ем), и конфигурация из 27 env-переменных не печатается
> на старте (PD-114). Фронт ждёт `POST /books` для экрана загрузки S4 (Ф-26 его бэклога).
## Обязательное чтение до кода (порядок; читать, не грепать)
1. `platform/docs/platform-PROGRESS.md` — «Текущее состояние» + разделы P4 и дофикса целиком
(формы раннера: холд+прогон+попытка+очередь одной транзакцией · порядок `lockBook` ·
`MaxAttempts:1` · ExecStopPost-маркер · `IsTransient` · PD-158/PD-159) + пинги оркестратора
в хвосте (формы артефактов движка: манифест `tm-manifest-v2`, `unit.id` с тегом разреза).
2. `platform/BACKLOG.md` (П-9 дословно — это заказ) + `platform/docs/DEFECT_REGISTER.md`:
PD-72 · PD-140 · PD-152 · PD-153 · PD-113 · PD-114 · PD-115 · PD-122 · PD-162 · PD-165 ·
PD-169 — и все открытые строки по разу.
3. Контракт (канон): `docs/architecture/14-api-contract/openapi.yaml``POST /books` (multipart,
`BookIntake`), статусы `uploading/parsing/rejected/not_started`, `POST /runs/{id}/stop` /
`resume` (409 при неполной подписи банка), `503`-класс; компаньон-README §3.
4. `platform/docs/ENGINEERING_STANDARDS.md` (критерии приёмки зоны) + `STACK_DECISIONS.md`
(пины, linger-модель §15, рецепт стенда с живым PG без root, ставка §20).
5. Движок (read-only, для стыка): `backend/cmd/tmctl/main.go:30-52` (exit-контракт; SIGTERM
ловится и выходит кодом 1) · `tmctl manifest` ($0-команда, строит дерево до первого прогона,
первое касание создаёт БД проекта — D39.122) · D39.110 п.2(б) (потолки `book.yaml` платформа
НЕ правит).
По всем задачам — прогон в отчёте; **молча пропустить задачу нельзя**: сделано / диспозиция /
вопрос в журнал.
## Задачи
### 1. `POST /books` — загрузка книги (П-9; закрывает PD-72)
- Multipart-приём с **пер-маршрутным потолком тела** (отраслевой stdlib-путь прежде велосипеда:
`http.MaxBytesReader` и потоковый разбор, не буферизация всего в память) + тест потолка —
PD-72 закрывается этим же паком.
- **Хранилище:** куда ложится исходник, кто и когда его чистит при отказе разбора и при удалении
(смежно PD-162: удалённый каталог книги сегодня клинит прогон с открытым холдом — реши хотя бы
диспозицией); относительные пути и `StateDir`-дисциплина зоны в силе.
- **Статусы жизненного цикла:** `uploading → parsing → not_started | rejected` получают ПИСАТЕЛЕЙ;
разбор = вызов движка ($0-команда `tmctl manifest` — она и создаёт БД проекта); отказ разбора =
`rejected` с продуктовой причиной без протечки внутренностей (ПТ-33); read-model один — книга,
заведённая дев-интейком, и книга, загруженная ручкой, читаются одинаково.
-**Развилка, ГЕЙЧЕНАЯ ратификацией: кто пишет `book.yaml` при интейке.** D39.110 п.2(б) —
«платформа `book.yaml` не правит» (контекст решения: потолок прогона не пишется в данные
движка). Создание НОВОГО каталога книги при аплоаде требует, чтобы кто-то положил стартовый
`book.yaml` (языки/жанр из `BookIntake`, потолки из деплой-шаблона). Спроектируй форму
(например: генерация из шаблона деплоя при интейке как отдельная операция, после которой файл
принадлежит оператору и платформа его больше не трогает), запиши варианты с ценой и **отправь
вопросом в журнал ДО стройки этой половины** — ратифицирует оркестратор. Остальное (приём,
потолок тела, хранилище, статусы, тесты) этой развилкой НЕ гейчено.
- `BookIntake.title` в спеке нет (черновик фронта ждёт ратификации) — поле НЕ выдумывать;
разбор даёт название, как даёт.
### 2. Стоп и резюм прогона (PD-140)
- Ручка стопа по контракту (точный путь и формы — ИЗ СПЕКИ, не отсюда): остановка юнита через
systemd (SIGTERM, graceful; движок дописывает чекпойнты и выходит), холд сеттлится штатным
реконсилятором.
- ⚠ Ограничение до эмиттера (строка 103 движка + 165): движок ловит SIGTERM и выходит кодом 1 ⇒
ExecStopPost-маркер честно скажет `exited/1`, и по одному маркеру стоп неотличим от аварии
(PD-152). **Кандидат-дизайн, оцени и предложи:** платформа ЗНАЕТ, что стоп её — записать
намерение стопа (строкой в Postgres той же транзакцией, что команда стопа) ДО SIGTERM и
классифицировать исход маркера по нему; это дискриминатор своей стороны, не гадание по
exit-коду. Если берёшь — пин на гонку «стоп против самостоятельного финиша». Различение
«потолок vs авария» остаётся за эмиттером — на него не замахиваться.
- `resume` по контракту (после банк-стопа — 409 при неполной подписи; после стопа — новый прогон
с остатком бюджета, механика перезапуска реконсилятора уже есть — переиспользовать, не
дублировать). Семантика денег прежняя: PD-158/PD-159 не трогать.
- Обе ручки — в аллоулисте форм контракта, wire-проверки в духе приёмки P4.
### 3. Наблюдаемость минимумом (П-11) + печать конфигурации (PD-114)
- Метрики зоны: глубина очереди · возраст самого старого открытого холда · число прогонов в
карантине · отставание тейлера · длительность/бюджет свипа (PD-169 смежно). Форму выбрать по
отраслевой практике с обоснованием и точным пином в `STACK_DECISIONS` (stdlib `expvar` против
prometheus-клиента — сравнить, решить, записать; PD-115: внешний эталон на ось наблюдаемости
назвать).
- Печать эффективной конфигурации на старте с редакцией секретов (PD-114 — задача уже
переформулирована ратификацией; деньги и argv в INFO по-прежнему не текут, PD-99).
### 4. Докс-минорка зоны: `DEFECT_REGISTER.md` секциями
Пинг оркестратора №16 (хвост зонного журнала): разложить реестр на секции (open по весу ·
accepted-risk · fixed по эрам паков), сохранив построчную форму `| PD-N | … |`;
`python3 docs/scripts/counts.py --check` обязан остаться зелёным (прогнать).
## Что НЕ делать
- Эскроу/`uncertain`/`closing` (строка 136 единого бэклога) — отдельный денежный промт; формулу
потолка (PD-158), SpendBound (PD-159) и ставку $0.03 не трогать.
- Эмиттер-сторону движка не строить и не ждать: тейлер уже умеет ждать файла; словарь `events.go`
не менять (движок ответит диффом со своей стороны).
- П-2 (брокер рейт-лимитов), платёжный провайдер, удаление аккаунта (PD-107 — гейт), счётчик
ревизии области (PD-122 — гейт «ручка удаления») — не в скоупе.
- Контракт не править: расхождение/нехватка — вопросом владельцу контракта через журнал
(прецедент 503/PD-112).
- Тесты и пины не подгонять под зелень (D39.121); `migrations.sha256` — только по правилам зоны.
- `git add`/`git commit` не делать — дерево передаёт на лендинг оркестратор.
## Отчёт и приёмка
- Батарея `make check` с живым PostgreSQL 18.4 под `-race`: 0 FAIL, линтер 0 issues, скипов 0,
`make vuln` чист; счётчик тестов — диффом `^func Test` исполнением, удалённых ноль.
- **Каждое новое свойство — с пином, каждый пин — с посадкой** (правило PD-83 зоны); посадки в
копии зоны вне репозитория, счёт «поймано/пережило» честно, пережившие не подчищать.
- **Живая проба на боевом бинаре** (фейковый движок как в P4): аплоад настоящего файла →
статусы `uploading/parsing/not_started` сменяются → книга видна в библиотеке и стартует прогон;
отказ разбора → `rejected` с продуктовой причиной; стоп живого прогона → юнит погашен, холд
сеттлится, статус по выбранному дизайну; метрики читаются; потолок тела отбивает большой файл
честным кодом ответа.
- Wire-сверка новых ручек с каноном спеки (форма ответов/ошибок problem+json, аллоулист полей).
- **Мандат самопроверки:** ревью ИСПОЛНЕНИЕМ + адверсариальное ревью диффа (author≠reviewer);
после тяжёлого ревью — записка-план «ID → статус → улика» (D39.121). Клеймы — с командой рядом;
перед сдачей перечитать последний абзац отчёта; комплектность против промта — механически.
- **Предметные оси самопроверки** (D39.120): (1) индустриальный первоисточник/stdlib прежде
велосипеда (multipart, метрики — назвать эталон); (2) деньги: ни один новый путь не создаёт
и не освобождает холд мимо ратифицированных форм; (3) жизненный цикл: каждая ветка нового
статуса достижима и покрыта (недостижимая ветка = дефект, класс PD-117…121).
- **Канал вопросов — зонный журнал, и у сессии есть право оспорить ПОСЫЛКУ** (D39.47 п.4):
замер или чтение кода, бьющие по основанию задачи (не по исполнению), обязывают остановиться
и поднять вопрос — а не выдать формально требуемый артефакт; тихая интерпретация запрещена.
- **Самоверификация интервально, не одним прогоном в конце:** после каждой задачи — сверка
с промтом и прогон гейтов; красное чинится до перехода к следующей.
- Отчёт — разделом в `platform-PROGRESS.md`: таблица задач · развилка book.yaml вопросом ·
дизайн стопа · закрытые/заведённые PD-строки. Реестр и счёт — скриптом, не руками.