textmachine/platform
2026-08-14 21:52:24 +03:00
..
cmd Land platform P5 after three acceptance rounds: streaming book intake with storage sentinel, stop and resume with recorded intent, Prometheus observability, enforced Go 1.26.6 floor 2026-08-14 02:24:08 +03:00
deploy Land platform P5 after three acceptance rounds: streaming book intake with storage sentinel, stop and resume with recorded intent, Prometheus observability, enforced Go 1.26.6 floor 2026-08-14 02:24:08 +03:00
docs Issue the P6 platform prompt: emitter seam consumer half, intake form B, seeded dev stand and the v15 deploy order, amended by the full-corpus review 2026-08-14 21:52:24 +03:00
internal Land platform P5 after three acceptance rounds: streaming book intake with storage sentinel, stop and resume with recorded intent, Prometheus observability, enforced Go 1.26.6 floor 2026-08-14 02:24:08 +03:00
.golangci.yml Land platform P0 skeleton: module, HTTP surface with security headers, server-side sessions with CSRF, read-model migrations, NDJSON ingest interface, zone battery and stack pins 2026-08-04 23:55:30 +03:00
BACKLOG.md Add platform row P-16: seeded dev stand with a test user so frontend development runs against the live platform instead of its own mocks 2026-08-14 21:08:53 +03:00
go.mod Land platform P5 after three acceptance rounds: streaming book intake with storage sentinel, stop and resume with recorded intent, Prometheus observability, enforced Go 1.26.6 floor 2026-08-14 02:24:08 +03:00
go.sum Land platform P5 after three acceptance rounds: streaming book intake with storage sentinel, stop and resume with recorded intent, Prometheus observability, enforced Go 1.26.6 floor 2026-08-14 02:24:08 +03:00
Makefile Land platform P5 after three acceptance rounds: streaming book intake with storage sentinel, stop and resume with recorded intent, Prometheus observability, enforced Go 1.26.6 floor 2026-08-14 02:24:08 +03:00
README.md Land platform P5 after three acceptance rounds: streaming book intake with storage sentinel, stop and resume with recorded intent, Prometheus observability, enforced Go 1.26.6 floor 2026-08-14 02:24:08 +03:00

platform — control plane (SaaS-слой)

Зона записи сессии «Платформа». P0 собран 04.08 (скелет: HTTP · сессии · схема read-model · интерфейс ингеста), P1 — 05.08 (вход через OIDC · кредитный леджер · админ-CLI · деплой-юнит · закрытие регистра дефектов). Направление зоны — docs/PLATFORM_DIRECTION.md, критерии приёмки — docs/ENGINEERING_STANDARDS.md, дефекты — docs/DEFECT_REGISTER.md, зонный журнал — docs/platform-PROGRESS.md (весь прогресс зоны здесь, решение владельца 04.08), стек — docs/STACK_DECISIONS.md.

Батарея зоны: make check (build · vet · fmt · lint · test -race). Тесты со схемой требуют TM_PLATFORM_TEST_DSN (как поднять Postgres без root — docs/STACK_DECISIONS.md); без него они пропускаются, и check называет пропуски вслух. make vuln и make fuzz — отдельными целями.

Бинари: cmd/tmplatformd (сервис) и cmd/tmplatformctl (админ: гранты, КОРРЕКТИРОВКИ (adjust), баланс, журнал входов, отзыв сессий, дев-интейк book add, и exit-markerего зовёт systemd на конце прогона). Деплой — deploy/.

Метрики — отдельным слушателем (TM_PLATFORM_METRICS_ADDR, дефолт 127.0.0.1:9464), формат Prometheus; на контрактную поверхность они не выходят и наружу не привязываются (docs/STACK_DECISIONS.md §24). Эффективная конфигурация печатается на старте с источником каждой настройки; секреты и денежные суммы — фактом наличия, без значения (PD-114).

Прогон — транзиентный systemd-юнит, а не ребёнок сервиса (D39.106). Установка требует loginctl enable-linger, иначе не стартует ни один прогон; почему именно так и что было измерено — docs/STACK_DECISIONS.md §1520.

⚠ Git и зона (читать ДО первой строки кода)

Платформенная сессия не коммитит — лендит оркестратор. Канон — ../CLAUDE.md, §Гардрейлы; продублировано здесь, потому что зона живёт своим онбордингом. Направление зоны (вход · деньги · стандарты · скорость) — docs/PLATFORM_DIRECTION.md; стандарты и критерии приёмки — docs/ENGINEERING_STANDARDS.md; дефекты и уязвимости — docs/DEFECT_REGISTER.md (каждая находка получает строку ДО закрытия).

  1. Писать только внутрь platform/. Ничего за её пределами — ни docs/, ни backend/, ни frontend/, ни корневых файлов. Нужна правка вне зоны — пинг владельцу, её сделает оркестратор.
  2. Чужие незакоммиченные файлы в дереве не трогать: параллельные сессии — норма.
  3. НИКАКИХ git add -A, reset --hard, amend/rebase, перезаписи истории и checkout поверх грязного дерева.
  4. ⚠ Ревью-гард модулей (D39.85): путь Go-модуля платформы никогда не вкладывать под путь движка — иначе он получит доступ к backend/internal/* по правилу префикса.

Что здесь будет

Сервис между фронтом и движком перевода. Всё, что относится к ПОЛЬЗОВАТЕЛЯМ и не относится к переводу:

  • аутентификация и аккаунты — есть (P1): вход через OIDC даёт только СОБЫТИЕ входа, сессия своя; ключ личности (provider, subject), почта не ключ. Оплаты нет и в бете не будет (владелец 05.08): аккаунты живут на кредитном балансе, фри-тир — запись grant в леджер;
  • библиотека книг: чья книга, права доступа, хранение исходников и экспортов — приём есть (P5): POST /v0/books принимает multipart потоково со своим потолком тела и своим дедлайном чтения, кладёт исходник в каталог книги под TM_PLATFORM_BOOKS_DIR и ведёт книгу по статусам uploading → parsing → not_started | rejected; разбор — $0-команда движка tmctl manifest. ⚠ Кто кладёт стартовый book.yaml в каталог новой книги — открытый вопрос на ратификации (D39.110 §2b): без него разбор честно отказывает, шов назван books.ErrNotProvisioned;
  • учёт денег на пользователясхема и операции есть (P1): append-only леджер в целых микро-долларах, резервации, кэш баланса с инвариантом balance == SUM(ledger). Защита прогона — холд ДО спавна плюс книжный потолок движку (жёсткий стоп исполняет движок). ⚠ Потолок, который получает движок, — это НАКОПЛЕННЫЙ потолок КНИГИ (committed + reserved за всю её историю плюс купленный прирост), а не бюджет прогона: платформа переводит одно в другое сама (D39.122);
  • остановка и продолжение перевода — есть (P5): POST /v0/runs/{id}/stop пишет НАМЕРЕНИЕ стопа в Postgres до сигнала (иначе стоп и авария — один и тот же выход движка, PD-152) и просит systemd; /resume открывает новую попытку с ОСТАТКОМ бюджета прогона, переиспользуя механику перезапуска реконсилятора;
  • очередь задач и запуск воркеров, статусы прогонов, ретраи — есть (P4): River на том же Postgres; задание очереди выдаёт только РАЗРЕШЕНИЕ стартовать, а жизнь прогона ведёт реконсилятор, который читает мир (Postgres · журнал книги · маркер выхода) и не ждёт процесса;
  • SSE-поток прогресса во фронт (⚠ денежные суммы на провод и на экран НЕ идут — D39.84; пользователь видит ОСТАТОК процентом — окон со сбросом больше нет, владелец 05.08).

Чего здесь НЕ будет

Перевода. Движок (../backend/) остаётся как есть: один процесс на книгу, свой SQLite под эксклюзивным flock (store.Open — «один процесс владеет файлом проекта»). Платформа его ЗАПУСКАЕТ как воркер, а не поглощает. Причина та же, по которой движок не ветвится по паре языков: пользователи и квоты ничего не меняют в проводе запроса, значит им нечего делать в кодовой базе, где каждый байт свёрнут в снапшот-хеш.

Известное требование к движку (не забыть)

Глобальный брокер конкурентности. pipeline/ratelimit.go строит рейт-гарды НА ПРОГОН, а лимит провайдера — на весь аккаунт. N параллельных пользователей = N независимых гардов против общего лимита (в комментарии там зафиксировано: mistral валит ~48% вызовов под параллелизмом). Воркер должен отпрашиваться у платформы перед вызовом. Это единственная по-настоящему новая механика на стыке.

Стек

Пины, даты релизов и обоснования — docs/STACK_DECISIONS.md (зонный, live-сверка 0405.08); общая записка по обоим новым сервисам — ../frontend/docs/STACK_DECISIONS.md §5.

Коротко: Go 1.26.4 в go.mod (тулчейн сборки ≥1.26.5) · стандартный net/http + ServeMux без роутер-библиотеки · PostgreSQL 18 · pgx v5.10.0 · goose v3.27.3 · очередь River v0.42.0 на том же Postgres (запинена, ещё не подключена — П-3) · вход x/oauth2 v0.36.0 + go-oidc/v3 v3.20.0 · x/time v0.15.0 для лимита на /auth/login · govulncheck отдельной целью. Redis не заводим нигде — зафиксировано как архитектурное «нет».

Прогресс наружу — SSE, события пушит воркер, а не фронт опрашивает read-model. Аутентификация — одна серверная сессия в Postgres, два способа предъявления: __Host-кука для браузера и Authorization: Bearer для десктопа и CLI; эндпоинты про куки не знают ничего.