textmachine/platform/docs/PLATFORM_P6_SESSION_PROMPT.md

196 lines
22 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.

# Промт: платформа P6 — половина шва эмиттера (П-15) · интейк формы Б (П-14) · дев-сид (П-16) · деплой-порядок v15
> Выдан оркестратором №16 14.08.2026. Запуск — по слову владельца. Курс владельца 14.08:
> **платформа достраивается ЦЕЛИКОМ до фронта** — фронт потом идёт по готовому и отлаженному,
> а не пушит решения в платформу (его пожелания заходят контрактом и PD-строками, как и раньше).
## 0. Какую проблему решаем и что даст твой результат
Платформа — SaaS-слой между веб-интерфейсом и движком перевода: пользователи, кредиты, очередь,
запуск движка транзиентными systemd-юнитами, read-model в Postgres. Построено много (P0P5:
вход · леджер · раннер · интейк книг · стоп/резюм · метрики), и 14.08 заленден ДВИЖКОВЫЙ эмиттер
событий (D39.131, `9cfe080`): движок теперь пишет `events.jsonl` (StreamVersion **1.1**) и
выходит РАЗЛИЧИМЫМИ кодами — 4 потолок · 5 graceful stop · 1019 полоса отказов. **Твоя зона
этих сигналов ещё не читает**: потолочный стоп у нас материализуется `failed` (единственный
major регистра PD-113 — контрактно видимая ложь: контракт запрещает называть резюмируемый стоп
`failed`), а `refusedTheSource()` не смотрит на код выхода вовсе — опечатка оператора в
`book.yaml` до сих пор стоит пользователю удалённого файла (PD-196). Плюс два хвоста
ратификаций: интейк должен сам рендерить стартовый `book.yaml` (форма Б, D39.130 — без этого ни
одна загрузка через UI не доезжает до разбора без ручной работы оператора), и стенду нужен
дев-сид с тестовым пользователем (слово владельца 14.08 — чтобы фронт разрабатывался на живой
платформе). Твой результат = платформа честно проживает все исходы движка, загрузка доезжает до
перевода без оператора, и на стенде можно жить тестовым аккаунтом.
Промт — твой ЕДИНСТВЕННЫЙ контекст; всё несущее — здесь или в названных доках.
## 1. Онбординг (порядок чтения, до кода)
1. `CLAUDE.md` (корень) — гардрейлы; жёсткое для тебя: зона записи ТОЛЬКО `platform/`, ты НЕ
коммитишь, `.env` не читать, чужие незакоммиченные файлы (в `docs/`, `eval/`, `frontend/`
там живут параллельные сессии) не трогать. `backend/` — read-only; свой `tmctl` для проб
собирай в копии ВНЕ репозитория (прецедент приёмки D39.131).
2. `platform/README.md` + `platform/docs/ENGINEERING_STANDARDS.md` (критерии приёмки — каждый
промт зоны обязан на них ссылаться; отступление = пинг) + `platform/docs/STACK_DECISIONS.md`
(пины · стенд PG без root · §1527 решения раннера/интейка).
3. `platform/docs/platform-PROGRESS.md` — шапка «Текущее состояние» + **пинг оркестратора 14.08
про эмиттер** (словарь событий финальный) + пинг про гигиену доков + записи P4/P5.
4. `platform/BACKLOG.md` (П-14 · П-15 · П-16 — твои задачи) + `platform/docs/DEFECT_REGISTER.md`
(секции открытых; целевые PD названы ниже).
5. Движковая правда шва: `docs/architecture/16-events-emitter.md` (норматив) + сам код движка
read-only — `backend/internal/runevents/` (`journal.go`, `runevents.go`),
`backend/internal/pipeline/events.go`, `pipeline/refusal.go`, `store/outbox.go`,
`cmd/tmctl` exit-коды. Код первичен.
**Эхо-протокол:** первый деливерабл — ≤10 строк «что я понял: скоуп / инварианты / не-делать»
ДО работы.
## 2. Задача 1 — П-15: потребительская половина шва эмиттера (несущий путь: деньги и статусы)
Движковая половина принята D39.131; словарь = ваш же `events.go`, принятый движком с диффом
(пинг 14.08 в журнале зоны: StreamVersion 1.1 · `Ceiling.Scope: book|day` ·
`Finished.outcome += ceiling|stopped` · `eta_seconds` = темп ТЕКУЩЕГО прогона · `unit_done.unit`
= лидерный `first_chunk_idx`). Делай РОВНО (ратифицировано):
- **(а) Маппинг exit-кодов движка.** Сегодня `outcome()` знает 0/2/3, всё прочее = `failed`.
Новая семантика: **4 = потолок**`paused` с причиной потолка, НИКОГДА не `failed`
(закрывает **PD-113**, единственный major); **5 = graceful stop по сигналу**; **1019 =
полоса отказов** с классами (10 конфиг · 11 «источник прочли и вердикт: книги в байтах нет» —
ЕДИНСТВЕННЫЙ класс, по которому интейк вправе удалить загрузку · 12 лок · 19 безымянный) —
`refusedTheSource()` судит по КОДУ, не по «exit 1» (закрывает потребительскую половину
**PD-196**: опечатка `book.yaml` больше не стоит файла). `paused_reason` читать ПОСЛЕ drain
журнала, не из stale-снапшота до него (находка приёмки D39.131 п.3).
⚠ Семантику «exit 5 БЕЗ записанного намерения стопа» (штатная перезагрузка хоста, чужой
SIGTERM) реши сам и запиши: приор — это прерывание, не авария и не пользовательский стоп ⇒
кандидат на перезапуск реконсилятором (путь строки 138), закрывает остаток **PD-152**;
опровергается аргументом.
- **(б) Фолд `unit_done` — ПРИСВАИВАНИЕМ по тройке (chapter, unit, wave), не инкрементом**
(ратифицировано D39.131 п.2г: поток at-least-once, идемпотентность — обязанность потребителя;
заодно самолечит потерю недодрейненного хвоста при рестарте).
- **(в) Словарь и версию читателя привести к потоку 1.1:** `ingest.StreamVersion` сегодня
`"1.0"` и словарь `events.go` не знает `Ceiling.Scope`/новых outcome — по мажору совместимо,
но читатель врёт о том, что понимает. **Принять `Ceiling.Scope` (book|day) и outcome
`ceiling|stopped`.** Scope = диагностика
**PD-157** (дневной потолок из `book.yaml` теперь различим); resume прогона, вставшего на
ДНЕВНОМ потолке, не гонять в цикл — реши форму (отказ с диагностикой / отложенный повтор),
аргументируй.
- **(г) Dev-супервизор `outcomeOf` стейл (знает 0/2/3)** — привести к той же семантике.
- **Watch (провериться, не обязательно строить):** foreign-hello adoption при пре-существующем
журнале в workdir — приёмка отметила как наблюдение; воспроизведёшь дефект — фикс с пином,
нет — запиши что проверено.
Деньги на этом пути живые: любое изменение веток закрытия прогона — «тяжёлый» класс по
`ENGINEERING_STANDARDS` §1 (полный цикл: пины + посадки + живая проба).
## 3. Задача 2 — П-14: интейк рендерит стартовый `book.yaml` (форма Б, ратификация D39.130)
Шов готов и назван — `books.ErrNotProvisioned` заменяется рендером в ОДНОМ месте
(`books.Service.manifest`-путь, твоя же оценка в развилке P5). Делай РОВНО:
- Рендер ОДИН раз при создании книги из деплой-шаблона (`TM_PLATFORM_BOOK_TEMPLATE` — файл
оператора): языки и жанр из `BookIntake`, `source_file` — имя, которое интейк сам записал,
потолки/пути пайплайна — из шаблона. Дальше файл принадлежит оператору: платформа его не
читает и не правит (D39.110 §2b в силе).
- Требования к полям — `backend/internal/config/book.go` как СПРАВОЧНИК (read-only): рендер
обязан давать конфиг, который движок грузит; проверь живым `tmctl manifest` своей сборки.
- Отсутствие/битость ШАБЛОНА — беда деплоя, не книги: не терминалить, не жечь бюджет попыток
(тот же предикат `waitsForTheDeployment`, что у пропавшего корня).
- Форма В (`tmctl init`, строка 170 единого) придёт позже — рендер должен сниматься заменой
одного места, это ревью-вопрос к твоей структуре.
## 4. Задача 3 — П-16: дев-сид стенда (слово владельца 14.08)
Цель: **фронт разрабатывается на живой платформе тестовым пользователем.** Состав:
- Одна команда/цель (`make seed` или `tmplatformctl`-подкоманда — реши форму): тестовый аккаунт
+ грант (админ-CLI уже умеет) + демо-книга через ЖИВОЙ интейк (не прямой INSERT — сид должен
ходить теми же путями, что пользователь).
- **Дев-вход без внешнего OIDC-провайдера.** Сверь, существует ли dev-профиль (П-6/PD-8 бэклога
зоны); в пробах сессию вставляли в Postgres руками — это не рецепт. Если нет — построй узкую
дев-форму с ЖЁСТКИМ гейтом: недостижима в проде ПО ПОСТРОЕНИЮ (например, монтируется только
при явной переменной + отказ, если одновременно задан боевой OIDC — форму реши сам,
security-ось ревью обязана её атаковать: дев-вход, доступный в бою, = захват аккаунта).
- Рецепт — в `deploy/` или `STACK_DECISIONS` (где живёт стенд), одной страницей: поднять PG →
мигрировать → сид → фронт через дев-прокси (`TM_PLATFORM` на стороне фронта уже есть).
- Границы (уже ратифицированы, не переоткрывать): фикстуры фронта остаются его батарее; снос
`frontend/src/mock/` — не твоя работа и не сейчас (триггер Ф-29 у фронта).
## 5. Задача 4 — деплой-порядок против деадлока v15 (строка 174 единого)
Факт: read-only `status` отказывает схеме старее бинаря, а ты зовёшь его ПЕРЕД каждым спавном и
для settle ⇒ деплой эмиттер-бинаря НА СТЕНД запирает все существующие книги. Решение владельца:
движку параллельно выдан микро-промт **`tmctl migrate`** ($0-команда, write-open без прогона).
Твоя часть:
- **Жёсткое правило: эмиттер-бинарь НЕ деплоится на стенд, пока `tmctl migrate` не заленден**
(или явное слово владельца). До того П-15 строится и проверяется против журналов, писанных
СВОИМ tmctl из копии `backend/` вне репозитория — ровно так делала приёмка D39.131.
- Деплой-док зоны фиксирует порядок апгрейда движка: стоп/дренаж прогонов → `migrate` по
каждой книге → новый бинарь в работу; шаг проверяется исполнением на стенде, когда migrate
приедет (если в твою сессию не успеет — порядок записан, проверка исполнением помечается
ждущей, НЕ клеймится сделанной).
## 6. Задача 5 — гигиена зонных доков (пинг оркестратора 14.08, список в журнале зоны)
Чинить по факту кода: `README.md:85` (тулчейн 1.26.6) · `README.md:87` и `STACK_DECISIONS.md:23`
(River «не подключён» против построенной P4-очереди) · `README.md:47-48` (вопрос book.yaml решён
D39.130 — а после твоей задачи 2 и построен) · `PLATFORM_DIRECTION.md` — противоречие
(oapi-codegen «взять — доказано» и sqlc «до первого хендлера» НЕ исполнены — ручки рукописные)
тихо не оставлять: либо исполняй на новых ручках, либо ПРЕДЛОЖИ пере-подпись направления с
причиной — ратифицирует оркестратор при лендинге, направление ратифицировано не зоной ·
**ре-диспозиция PD-60 и PD-61**: обе стоят open, а их движковая половина решена и залендена
D39.131 (PD-60 — деградация стала громкой; PD-61(а) — буфера в эмиттере нет вовсе) — закрыть
либо пере-формулировать остаток по факту кода · `DEFECT_REGISTER.md`: диспозиции D39.130 в PD-172/173/174/180 (спек-правка
0.2.3 = задача S4 фронта, идёт параллельно) · открытые PD-180/185/196 из секций «Закрытые…» — в
открытые · порядок PD-178 · путь counts.py · веса секций · `STACK_DECISIONS.md:17`
(«make tools-check» → `version-check`) · П-1/П-3 бэклога не знают построенного P4 · хвост (г)
третьего раунда (перечислительность мета-пина systemd) — PD-строка или явное закрытие.
## 7. Попутные PD того же касания — по весу, реши сам (взять либо диспозиционировать, не молчать)
Все живут в файлах, которые ты и так трогаешь: **PD-141** (`PauseRun` молча возвращает nil на
завершённом прогоне) · **PD-164** (существующий, но неразбираемый маркер вечно валит
реконсиляцию — нужен аналог карантина) · **PD-165** (относительный `TM_PLATFORM_CTL_BIN` — отказ
на буте, как у StateDir) · **PD-163** (ревизия области читается вторым запросом после страницы)
· **PD-154** (окно `Settle`/`MarkSettled`). Расширять пак сверх этого списка НЕ надо (класс
PD-83): нашёл новое — строкой в регистр.
## 8. Чего НЕ делать
Читающая поверхность (SSE-эндпоинт, ручки глав/юнитов/замечаний, проекция `bank.json`) — это
СЛЕДУЮЩИЙ пак (P7), не тяни её сюда. Эскроу/`uncertain`/`closing` (строка 136) — денежный промт
позже. Контракт (`openapi.yaml` канон и зеркало) не трогать — его прямо сейчас правит S4-сессия
фронта в своей зоне. П-2 (брокер) — гейт «до второго параллельного пользователя» не наступил.
Платёжного провайдера не существует. Чужие незакоммиченные файлы в `docs/`/`eval/`/`frontend/`
не трогать — там живые сессии.
## 9. Самопроверка и сдача (мандат; DoD = `ENGINEERING_STANDARDS` §3 целиком)
- **Исполнением, не чтением:** батарея `make check` с живым PG (`~/.local/pgsql`, порт 55433,
рецепт в STACK_DECISIONS) под `-race`, скипов 0 · живая проба на собранных бинарях с фейком
движка, моделирующим СЕМАНТИКУ (exit-коды 4/5/1019, кумулятивный счётчик — фейк, который не
может отказать, не проверяет ничего: урок D39.123) · каждый новый инвариант запинен тестом,
сессия называет «какой тест что пинит» · посадки своих мутаций с восстановлением, списки
посадок — В ЖУРНАЛ (не в скрэтчпад: урок дофикса-2).
- **Деньги — двумя независимыми путями счёта**; леджер = нижняя граница.
- **Адверсариальное ревью диффа ДО сдачи — механизмом, author≠reviewer:** независимые
ревью-агенты по диффу с установкой опровергать; субагенты/параллельные агенты **разрешены
явно**. В панели — ≥1 опровергатель ДРУГОЙ модели на оценочных линзах (D39.120; в этой зоне
это эмпирически доказано: кросс-семейный круг дофикса-2 нашёл 5 дефектов, которых три
однофамильца не увидели, два — уничтожение данных). Артефакт — таблица «находка → диспозиция»
в журнале.
- **Предметные оси этого пака** (вправе добавить/аргументированно снять): (1) индустриальный
первоисточник прежде велосипеда (норма зоны §1); (2) деньги и гонки — каждый новый путь
закрытия прогона атакуется устаревшим снапшотом/двумя поколениями свипа (класс PD-181/193);
(3) безопасность дев-входа — атаковать достижимость в проде.
- **Заявление = команда**; числа с командами; «чего НЕ сделал» — явным списком; записка-план
ID → статус → улика по каждой задаче и каждому PD.
- Сдача: запись в `platform-PROGRESS.md` (механика, пробы, посадки, диспозиции), регистр
обновлён строками тем же деревом, `python3 docs/scripts/counts.py --check` зелёный, дерево НЕ
коммитить — лендит оркестратор после адверсариальной приёмки.
## 10. Канал вопросов
Непонятно / промт конфликтует с кодом или доками / нужна правка вне `platform/` → пинг
оркестратору через владельца и запись в зонный журнал; НЕ интерпретация. Настоящие развилки
эскалируй быстро — это норма (образец: развилка book.yaml в P5, которую сессия правильно НЕ
интерпретировала тихо).