textmachine/platform/docs/ENGINEERING_STANDARDS.md

79 lines
9.4 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.

# Стандарты разработки и критерии приёмки платформы
> Ратифицировано оркестратором №14 по решению владельца 04.08: платформа считает токены пользователей
> и держит их логины — «ошибки недопустимы»; качество и отказоустойчивость выше скорости фич.
> Каждый промт платформенной сессии обязан ссылаться на этот документ; отступление = пинг, не тихая девиация.
## 1. Рамка
- **Несущие пути** — auth/сессии/CSRF, всё денежное (usage, лимиты, биллинг), ингест/супервизия движка,
миграции. Изменение несущего пути = «тяжёлый» класс: полный цикл приёмки §3, без исключений.
- **Сторонние тулы прежде самописного** (решение владельца 04.08): проверенные библиотеки предпочитаются
своему коду везде, КРОМЕ похода в ядро переводов (tmctl зовётся процессом, обвязка своя) и мест, где
stdlib и есть индустриальный стандарт (net/http, database/sql-слой через pgx). Новая зависимость —
строка в `STACK_DECISIONS.md` с датой релиза и «зачем», ратифицирует оркестратор.
- Канон-инварианты зоны без изменений: D39.84 (никаких денежных сумм на проводе/UI/INFO-логах) ·
D39.85 (шов: NDJSON → апсерт `(engine_run_id, seq)` → SSE из Postgres; живой SQLite движка не читать) ·
ПТ-34 (noindex/no-store на всём) · языко-агностичность.
## 2. Базовые стандарты (проверяются каждой приёмкой)
**Безопасность** — базовая линия OWASP **ASVS 5.0.0, целевой L2** (главы, релевантные зоне: V6
аутентификация · V7 сессии · V10 OAuth/OIDC) + OWASP API Security Top 10 2023 (API1 BOLA = проверка
владения книгой на каждом `/v0`-маршруте; API4 = класс PD-2). Что берём и чего не берём из
инструментов — `PLATFORM_DIRECTION.md` §3. Конкретика:
- Сессии: одна серверная, opaque-токен ≥256 бит из crypto/rand, в БД только хеш, немедленный отзыв,
два срока (idle скользит, absolute нет). Плейнтекст-токен не персистится, не логируется и живёт
в памяти только окно обработки запроса (решение владельца 04.08).
- Секреты — только из окружения/секрет-менеджера; в репо и в логи не попадают НИ на каком уровне.
- Ввод: только параметризованные запросы; каждый body-принимающий хендлер несёт `http.MaxBytesReader`
и дедлайн (гейт PD-2 регистра); закрытые словари валидируются на входе И констрейнтами БД.
- Ответы: problem+json; внутренности и оракулы (unknown-vs-expired токен) на провод не текут;
заголовки ПТ-34+nosniff+no-referrer — мидлварью на всё, не дисциплиной хендлера.
- `govulncheck` (`make vuln`) — перед каждым лендингом; красный = блокер лендинга.
**Отказоустойчивость:**
- Ингест идемпотентен (`(engine_run_id, seq)`, high-water mark в одной транзакции с эффектом);
at-least-once — норма, дубль — не ошибка.
- Graceful shutdown с реальным дренажем; пул с лимитами; транзиентные ошибки БД — ретраи с джиттером;
миграции идемпотентны, down-путь существует и гоняется тестом.
- Отказ зависимости — деградация с диагностикой, не тишина: проглоченная ошибка без лога и без
комментария-обоснования = дефект (класс PD-5/PD-16).
**Наблюдаемость** — базовая линия **практики именования Prometheus** (базовые единицы: секунды и
байты; `_total` у счётчиков; единица в имени, не в лейбле) + **четыре золотых сигнала** SRE на
вопрос «что мерить» (внесено P5 по ратифицированному направлению PD-115: у оси появился внешний
эталон, а не только собственная проза зоны). Конкретика:
- структурные логи (route-pattern, не сырой путь — id пользователя/книги в логи не текут),
`X-Request-Id` на каждом ответе, ошибки видимы на своём уровне;
- метрики отдаются в формате Prometheus на ОТДЕЛЬНОМ слушателе (`STACK_DECISIONS` §24); лейбл несёт
паттерн маршрута, никогда путь — иначе это и неограниченная кардинальность, и библиотека
пользователя в индексе оператора;
- Money-числа — только в приватных таблицах, ни в логах, ни в ответах, ни в метриках (кроме
P-5-статуса процентами);
- ⚠ оси **ops** (релиз/откат, ёмкость, восстановление) и **конфигурация** внешнего эталона
по-прежнему не имеют — PD-115 открыт ими.
**Контракт-первичность:** поверхность = `docs/architecture/14-api-contract/openapi.yaml`; расхождение
кода со спекой = дефект чей-то один: либо спека правится через ратификацию, либо код. Генерация
типов/стабов из спеки (oapi-codegen — кандидат, ратификация при P1) предпочтительнее ручного дрифта.
## 3. Критерии приёмки сессии (Definition of Done)
1. `make check` зелёный офлайн; скипы названы вслух. ⚠ **«Скипов ноль» требует ТРЁХ условий, а не
одного** (испр. 22.08 — прежняя редакция называла только DSN и позволяла объявить приёмку
выполненной с молча пропущенной третью батареи): `TM_PLATFORM_TEST_DSN` (схема) ·
`TM_PLATFORM_TEST_ENGINE_BIN` + `TM_PLATFORM_TEST_BOOK_TEMPLATE` (живой рендер конфигурации) ·
ДОСТИЖИМЫЙ пользовательский менеджер systemd (`internal/runner`, `systemdOrSkip` — на хосте без
logind-сессии три теста скипаются и «скипов 0» недостижимо в принципе, `PD-374`). Условие, которое
на этом хосте не выполнено, сессия НАЗЫВАЕТ вместе с числом скипов — «зелёная батарея» без него не
значит ничего. Порядок подъёма PG без root — `STACK_DECISIONS.md`.
2. Каждый деливерабл проверен ИСПОЛНЕНИЕМ (сервер поднят и опрошен, миграции применены дважды,
констрейнты сработали поимённо, супервизор гонял настоящий процесс) — не чтением.
3. Заявленное в отчёте свойство несущего пути ОБЯЗАНО быть запинено тестом, который ловит мутацию
этого свойства; сессия сама называет в отчёте, какой тест что пинит. Приёмка оркестратора сажает
СВОИ мутации (урок P0: «в БД только хеш» было истинно, но не запинено — посадка выжила).
4. Адверсариальная самопроверка (author≠reviewer) до сдачи; найденное — в отчёт, не молча.
5. Новые находки — строками в `DEFECT_REGISTER.md` тем же деревом; закрытие дефекта — коммит + пинящий тест.
6. Дерево не коммитится сессией — лендит оркестратор после адверсариальной приёмки
(для несущих путей — с воркфлоу-верификаторами и посадками).