docs(ai): add scoped backend conventions

This commit is contained in:
2026-09-09 16:23:03 +03:00
parent 320e8a04b0
commit a83e26e5ef
10 changed files with 377 additions and 36 deletions
+24
View File
@@ -0,0 +1,24 @@
# Backend router
Объединяй triggers из смысла задачи, путей и обнаруженных boundaries.
Читай выбранные guides до решения; якорь допускает чтение только нужного раздела.
Просто backend-файл не требует всех conventions.
| Решение / сигнал | Читать |
| --- | --- |
| Новый module/provider/service; бизнес-логика; перенос responsibility | [Architecture](conventions/backend-architecture.md) |
| Controller, DTO, route/status, validation/exception, публичная HTTP-семантика | [HTTP API](conventions/backend-http-api.md) |
| Prisma/schema/query/persistence, transaction, migration, atomic write | [Data](conventions/backend-data.md) |
| Тесты или behavior с testing risk | [Testing](conventions/backend-testing.md) |
| JWT, cookie, login/refresh/logout, guard/access, token encryption, sensitive logs | [Security](conventions/security-auth.md) |
| T-Bank/gRPC/proto, upstream retry/rate limit/cache, MoexClient | [Integrations](conventions/backend-integrations.md) |
| Logging/request context без изменения secrets/auth | [Logging](conventions/backend-architecture.md#logging) |
| Новый abstraction/dependency, refactor, неясный scope | [Общий выбор реализации](../../docs/development/engineering-governance.md#выбор-реализации) |
| Изменение request/response/route/status/error shape или Swagger metadata | HTTP API + [общий contract workflow](../../docs/development/runtime-openapi-verification.md) |
Новый endpoint обычно требует HTTP API + Testing; Architecture/Data/Security
добавляются по новым ответственности, persistence или access rules.
Изменение одного Prisma query без HTTP/auth границы не требует HTTP API/Security.
Auth refresh активирует Security + Data + Testing; HTTP API добавляется, если
меняется controller/HTTP contract. Exact docs/spec sources выбирай через SOURCES.
Повтори routing при расширении diff, включая новые и unstaged файлы.
+19
View File
@@ -0,0 +1,19 @@
# Источники backend
| Что нужно установить | Владелец |
| --- | --- |
| Implementation decision и безопасный pattern | [CONVENTIONS](CONVENTIONS.md) |
| HTTP errors, DTO semantics, public wire shape | [docs: HTTP API](../../docs/AI_INDEX.md#http-api); runtime OpenAPI из Nest — единственный editable source |
| Auth/session, account access, token encryption | [docs: Security](../../docs/AI_INDEX.md#security) |
| Prisma schema/data ownership и migration context | [docs: Data](../../docs/AI_INDEX.md#data) |
| T-Bank/gRPC/cache, logging | [docs: Integrations](../../docs/AI_INDEX.md#integrations) |
| Money, account/position/income/cache meanings | [docs: Domain](../../docs/AI_INDEX.md#domain) |
| Поведение конкретной feature | [docs: Features](../../docs/AI_INDEX.md#features) |
| Существующие behavioral requirements при необходимости | [specs index](../../specs/AI_INDEX.md) |
| Versions/scripts и scope автоматических checks | package.json/lockfile, ESLint/TypeScript/Jest/Prisma configs |
SOURCES — карта, не требование прочитать все ссылки. Code показывает реализацию,
ADR — rationale, docs — текущие факты; старый пример не отменяет контракт.
Исследование предлагает варианты, а actual source разрешает выбор. При missing
или conflicting security/data facts сначала установи источник. Для обычной
работы OpenSpec/SDD не требуется.
+53
View File
@@ -0,0 +1,53 @@
# Backend architecture
## Выбор владельца
Найди существующий use case, его collaborators и tests до создания module/service.
Сохрани ответственность в текущем module, если новая возможность относится к нему.
Module — граница связного набора providers, а не папка для каждого endpoint.
| Решение | Owner |
| --- | --- |
| Route, request/response mapping, status/cookie | Controller, [HTTP rules](backend-http-api.md) |
| Business use case, orchestration, domain validation | Feature service |
| Input shape/format | DTO + configured ValidationPipe |
| Атомарность/DB query | Сервис use case через PrismaService, [Data](backend-data.md) |
| Внешний protocol, deadline/retry/limiting | Existing client, [Integrations](backend-integrations.md) |
| Чистое вычисление/mapper без lifecycle и DI | Обычная функция рядом с domain owner |
| Bootstrap/config/global cross-cutting concern | AppModule, config, существующий common owner |
**Безопасный путь:** расширь текущий owner; выделяй collaborator, когда он скрывает
самостоятельный инвариант или координацию. Не вводи CQRS, generic repository,
interface с одной реализацией или base controller только ради шаблона Nest.
Не превращай common в хранилище бизнес-логики и не переноси frontend модели сюда.
## Существующие seams
`TbankConnectionModule` уже объединяет счета, портфель, операции, доходы и события.
Новый связанный use case обычно добавляется в него; создание отдельного module
требует собственной ответственности, не только нового файла.
- [BrokerageOperationsController](../../src/tbank/brokerage-operations.controller.ts) передаёт user/account/query в service, без финансовых вычислений в controller.
- [TbankAccessService](../../src/tbank/tbank-access.service.ts) владеет connection/account access и generation; новые account use cases используют его вместо копирования guard/decrypt logic.
- [decimal.ts](../../src/tbank/decimal.ts) — пример pure domain helper; Injectable и adapter для арифметики не нужны.
Наличие guard подтверждает session, но не владение account. При пересечении
access boundary добавь [Security](security-auth.md). Ошибка upstream не должна
обходить общий HTTP filter или превращать чужой account в доступный.
## Logging
Единственный logger — nestjs-pino; application events используют существующий
[StructuredLogger](../../src/common/logging/structured-logger.service.ts) и
request context. Выбирай стабильное имя события и разрешённые scalar fields.
Wrapper отбрасывает неизвестные поля, но это не разрешение помещать secret
в `message`/`stack`: review sensitive logging обязателен.
Не добавляй второй logger или сырой request/body/error payload.
Источник rationale — [ADR 008](../../../docs/adr/008-pino-backend-logging.md);
при secrets/user data читай также Security.
## Проверка
`npm run typecheck:app`, `npm run lint`, targeted Jest по изменённому use case.
Смысл boundaries проверяется review: backend не имеет отдельного автоматического
FSD/DI architecture checker. Existing tests и build не доказывают правильный owner.
+56
View File
@@ -0,0 +1,56 @@
# Backend data
## Ownership и выбор операции
PrismaService — существующий shared DB provider с lifecycle приложения.
`prisma/schema.prisma` и migrations владеют persistence; `generated/prisma`
создаётся Prisma и не редактируется вручную. Конфигурация URL находится в
`prisma.config.ts`/validated environment; SQLite — текущая локальная база.
| Ситуация | Решение |
| --- | --- |
| Один query/write конкретного use case | Existing service + PrismaService |
| Несколько записей обязаны выполниться вместе | Database transaction вокруг инварианта |
| Конкурентная смена одного значения | Atomic conditional update/constraint с проверкой результата |
| Независимые reads | Не добавляй transaction ради внешнего вида |
| Общая persistence abstraction | Только при реальной общей ответственности; generic repository для одной Prisma реализации не нужен |
Выбирай только нужные данные, сохраняй user/account ownership в запросах и
application checks. Предварительный lookup не заменяет unique constraint при race.
Пример: AuthService обрабатывает P2002 и использует conditional `updateMany`
для refresh rotation. Сначала пойми invariant; не копируй его flow в другой use case.
Transaction не охватывает сетевой T-Bank вызов как будто это одна atomic DB
операция. Планируй порядок и compensation по текущей задаче; не удерживай DB
lock во время retry внешней сети. Access/cache generation semantics — у
[T-Bank source](../../../docs/features/tbank-connection.md#граница-финансового-кэша).
## Деньги и точность
Для денежных значений используй existing decimal helpers и строковый wire value,
не JS floating-point арифметику для сумм. Currency — отдельная часть Money;
разные валюты не складываются без явно заданного conversion contract.
Источник: [Money](../../../docs/architecture/domain-glossary.md#money),
пример: [decimal.ts](../../src/tbank/decimal.ts). UI formatting не меняет
backend ownership расчётов и доменную семантику.
## Schema и migration
Сначала определи, какие существующие данные/consumers должны сохраниться.
Миграцию проверяй на disposable копии с representative existing data; generated
client обновляй из schema. Не переписывай уже применённую migration ради нового
состояния и не выполняй reset/drop реальной базы как шаг верификации.
Для destructive или compatibility-sensitive изменения подготовь backup/restore,
переход и проверку отката до его применения. SQLite/local MVP не означает disposable data.
**Безопасный путь:** additive изменение, заполнение/проверка данных и только затем
удаление прежней формы, если задача этого требует. Примеры schema/migrations:
[Prisma source](../../prisma/schema.prisma); исторический rationale —
[ADR 001](../../../docs/adr/001-prisma-sqlite-local-mvp.md), не текущий список таблиц.
## Проверка
`npx prisma validate`; после schema change `npx prisma generate` и
`npm run typecheck:app`. Invariants/atomicity/migration safety требуют tests
на соответствующей boundary: mock Prisma не доказывает реальную transaction.
Выбор integration/e2e и известные ограничения — в [Testing](backend-testing.md).
+52
View File
@@ -0,0 +1,52 @@
# Backend HTTP API
## Выбор boundary
Сначала определи, меняется wire contract или только реализация существующего
use case. Для route/request/response/status/error shape читай
[HTTP semantics](../../../docs/architecture/http-api.md) и
[общий contract workflow](../../../docs/development/runtime-openapi-verification.md).
| Проверка | Где выполнять |
| --- | --- |
| Тип/формат/диапазон входа | Runtime DTO class с class-validator/class-transformer |
| Согласованность полей, бизнес-ограничение | Feature service/domain helper |
| Session и identity | Existing guard/strategy |
| Доступ user к конкретному account | Existing access service |
| HTTP envelope/безопасная ошибка | Общий HttpExceptionFilter |
Controller объявляет contract и делегирует; не обращается напрямую к Prisma/gRPC
ради обхода service. Request identity берётся из verified `request.user`, не из
body/userId, предоставленного клиентом. Access rules активируют Security.
## DTO и errors
DTO — runtime class: TS interface не предоставляет validation metadata.
ValidationPipe уже настроен в `configureApp` (`transform`, `whitelist`).
Не обещай rejection неизвестных fields: `forbidNonWhitelisted` не включён.
Для query numbers/arrays выбирай явное преобразование и валидаторы, как в
[operations DTO](../../src/tbank/brokerage-operation.dto.ts). Swagger metadata
описывает те же required/optional/nullable/enum/range значения.
Используй типизированные response DTO, `ApiOkResponse`/другой фактический status
и существующий `ApiProblemResponses`. При новых ошибках задай публичный stable
`code`; сообщение внутреннего exception не является UI contract. Не возвращай
raw upstream error, Prisma record с secrets или stack. Текущая форма ошибок
описана у HTTP owner; не добавляй поля RFC только потому, что они встречаются в tutorial.
**Безопасный путь:** используй exception и общий filter, а не локальный
`res.status(...).json(...)`. Исключение — транспортная операция вроде cookie;
следуй существующему auth controller с `passthrough` и явно заданным status.
## Пример и проверка
[Operations controller](../../src/tbank/brokerage-operations.controller.ts)
показывает guard, user.sub, DTO query и service delegation.
Для нового field обновляй canonical source и generated artifacts в определённом
направлении; body type frontend не исправляется вручную.
Проверь happy path и применимые invalid input/access/not-found/domain/upstream
errors на boundary, где они возникают. Unit вызов controller не запускает pipes,
guards или serialization. `npm run test:openapi-contract` проверяет schema parity,
но не поведение invalid request; выбирай соответствующий test по
[Testing](backend-testing.md). Финальный gate не заменяет semantic review ошибок.
+47
View File
@@ -0,0 +1,47 @@
# Backend integrations
## Выбор boundary
T-Bank gRPC принадлежит TbankClient; MOEX metadata — MoexClient. Сервисы use case
получают нормализованный результат через эти clients. Frontend не вызывает
T-Bank и не получает credentials/proto client. Reference source —
[ADR 003](../../../docs/adr/003-tbank-server-side-grpc-boundary.md) и
[connection behavior](../../../docs/features/tbank-connection.md).
Для нового account use case сначала используй TbankAccessService: access,
connection generation и invalid-token translation не дублируются в callers.
Чистый mapper финансового значения остаётся рядом с domain owner.
## Retry, cache и protocol
Existing client уже владеет bounded FIFO queue, rate limit, deadline и retry
классификацией. Новый RPC проходит тот же путь; не добавляй вложенный retry loop
в service. Не отключай TLS verification ради окружения: используй документированный
CA mechanism. Точные параметры и ограничения принадлежат
[connection configuration](../../../docs/features/tbank-connection.md#конфигурация-и-ограничения).
При query/cache change установи key, user/account/generation ownership,
freshness и поведение late response. Старый результат не может обслужить новую
connection generation. Client Cached Snapshot frontend отличается от backend
cache/stale policy; semantics конкретного use case читай через
[feature index](../../../docs/AI_INDEX.md#features).
Внешний auth failure и временная недоступность имеют разные public codes;
raw gRPC exception не уходит клиенту. Existing translation — в TbankTokenService
и общем HTTP boundary. Для HTTP error change читай HTTP API/Security по trigger.
Vendor proto хранится в `src/tbank/proto/contracts`; для обновления сверяй
официальный source и влияние на loader/DTO, не редактируй read-only references.
Generated artifacts, если есть, создавай из source, без ручной подгонки вывода.
## Безопасный путь и проверка
Сначала добавь нужный operation в существующий client и используй его из
ответственного service. Отдельный abstraction/adapter оправдан новой
ответственностью или несовместимым protocol, не только вторым методом.
[TbankClient tests](../../src/tbank/tbank.client.spec.ts) — пример controlled
upstream responses. Проверь queue/deadline/retry limit, token translation,
cache isolation и late response для затронутых ветвей. Fixtures синтетические;
реальная сеть, секреты и пользовательские accounts не нужны.
Logging использует [existing logger](backend-architecture.md#logging).
+46
View File
@@ -0,0 +1,46 @@
# Backend testing
## Выбор уровня
Применяй [общие testing risks](../../../docs/development/engineering-governance.md#risk-based-testing).
Тесты должны различать правильное и неправильное наблюдаемое поведение.
| Что меняется | Проверка |
| --- | --- |
| Pure mapper/decimal/domain branch | Unit со значимыми edge cases |
| Service orchestration/access/retry policy | Jest с mocks внешних boundaries и observable outcome |
| Pipe/guard/cookie/status/HTTP error | Configured Nest HTTP boundary; прямого вызова метода controller недостаточно |
| OpenAPI metadata/wire schema | `test:openapi-contract` + потребительский contract check |
| DB constraint/CAS/transaction/migration | Integration с изолированной реальной DB |
| Только DTO declarations/thin wiring | Existing contract/type/build без искусственного unit test |
Unit specs colocated в `src/**/*.spec.ts`; HTTP/integration — `test/`.
Мокируй T-Bank, время и нужную DB boundary, а не проверяемую business-функцию.
Не зови реальный внешний account/API. Expected error/code, отсутствие запроса
при недопустимом доступе и сохранение данных важнее private method call count.
## Случаи и примеры
- [BrokerageAccountsService tests](../../src/tbank/brokerage-accounts.service.spec.ts): отсутствие connection не вызывает upstream, нормализация и точные суммы.
- [AuthService tests](../../src/auth/auth.service.spec.ts): invalid/expired/superseded token и concurrent rotation; mock race test не является доказательством DB transaction.
- [HTTP filter tests](../../src/common/filters/http-exception.filter.spec.ts): безопасный error envelope.
- [OpenAPI integration](../../test/openapi-contract.e2e-spec.ts): configured app + frontend snapshot, без записи snapshot и без T-Bank.
При bug сначала воспроизведи именно его исход, затем исправь responsible owner.
Не создавай tests для каждой декларации, logs строки или поведения Prisma/Nest
без собственного инварианта. Coverage не имеет общего threshold.
## Команды и ограничения
Итерация: `npm test -- --runInBand <pattern>`, затем нужный typecheck/lint.
Полный `npm run verify` включает lint, оба typecheck, Jest и build.
`npm run test:openapi-contract` — отдельный required check при wire contract change.
`npm run test:e2e` существует, но известный Prisma migration blocker описан в
[backend workflow](../../../docs/development/backend-workflow.md) и WL-P2-05
[registry](../../../docs/development/audit-remediation.md). Скрипт использует
фиксированную `/tmp/wealth-ledger-e2e.db`; не запускай его конкурентно как
независимые DB tests. Для isolation выбери отдельную test DB и runner configuration.
Если задаче нужна HTTP/DB proof, установи применимость blocker и выполни доступную
изолированную проверку. Не объявляй покрытым то, что до tests не запустилось;
сохрани точный failure и оставшуюся проверку. Unit/Swagger success не заменяет e2e.
+51
View File
@@ -0,0 +1,51 @@
# Security и auth
Читай при изменении session/token/cookie/guard, account access, encryption или
sensitive logging; обычный data query без этой границы не требует всего документа.
## Выбор владельца
Identity/session — AuthService + strategy/guard; cookie transport — AuthController;
account access/connection generation — TbankAccessService; token crypto —
TbankTokenService/TbankCrypto. Расширяй существующую boundary, не добавляй
второй JWT middleware, decrypt helper или refresh coordinator.
[Authentication](../../../docs/features/authentication.md) и
[ADR 006](../../../docs/adr/006-auth-session-policy.md)/[009](../../../docs/adr/009-concurrent-refresh-rotation-policy.md)
задают session semantics. Access хранится только в памяти frontend; refresh
передаётся httpOnly cookie. Source code конфигурации cookie —
[AuthController](../../src/auth/auth.controller.ts); изменение flags/path требует
проверки browser/HTTP поведения, не только TypeScript.
Refresh rotation атомарна: один победитель, проигравший/superseded token получает
401, но сам по себе не отзывает session победителя. Не вводи replay history/grace
или иной security contract без текущей задачи. Logout идемпотентен; in-memory
frontend session/cache очищается и при network failure.
## Trust boundaries
Verified JWT не доказывает доступ к accountId из URL. Используй user.sub и
existing account access policy; не доверяй userId/token из клиента как authority.
Cross-user, invalid/expired/superseded token и concurrent requests — обязательные
cases для соответствующего изменения.
T-Bank credentials остаются server-side и хранятся encrypted. Используй
existing AES-256-GCM boundary с validated 32-byte key; не печатай plaintext,
шифротекст, cookie, Authorization или connection generation в logs/errors/API.
[Connection source](../../../docs/features/tbank-connection.md) определяет
cache invalidation при замене/deletion и ограничение key rotation.
Не передавай Prisma User/Session целиком наружу; выбирай public DTO. Пароль
обрабатывает существующий bcrypt flow. Не копируй требования о нормализации
или длине из tutorial: сверяй runtime DTO, tests и feature source.
## Безопасный путь и проверка
При неясной ownership/crypto/session семантике прочитай exact source до edits.
Добавляй проверку на ответственной границе; не скрывай проблему общим catch,
фиктивным success или log. Logger allowlist/redaction не делает произвольный
message безопасным.
Targeted auth/access/crypto/logging tests + [Testing](backend-testing.md).
Cookie/guard/HTTP changes требуют configured HTTP proof; frontend Bearer/retry
проверяется MSW. Schema parity не доказывает отсутствие утечки secret.
+17 -31
View File
@@ -1,37 +1,23 @@
# Backend
## Назначение и authority
NestJS API; общие правила работы — в root `AGENTS.md`.
До backend-решения прочитай [router](.ai/CONVENTIONS.md) и
[sources](.ai/SOURCES.md), затем только применимые документы/разделы.
NestJS API WealthLedger владеет доменной логикой, данными, конфигурацией и
server-side интеграциями. Workspace layout и reference policy — в root
`AGENTS.md`. Для planning, implementation, validation и завершения читай
[canonical lifecycle](../docs/development/openspec-authority-and-lifecycle.md);
для recovery и выбора relevant evidence —
[context discipline](../docs/development/agent-token-efficiency.md).
Они доступны без optional agent skills или commands.
## Проверка
Для устойчивых backend decisions читать [`../docs/adr/README.md`](../docs/adr/README.md).
Перед Node-командами: `nvm use --silent` по `.nvmrc`.
После lockfile install `npm ci` полный gate — `npm run verify`:
zero-warning ESLint, application/test TypeScript, Jest и build.
`lint:fix`/`format` пишут файлы и запускаются отдельно от доказательства проверки.
`test:cov` — диагностика без общего threshold; scripts принадлежат `package.json`.
## Canonical gate
HTTP contract changes дополнительно проходят общий
[contract workflow](../docs/development/runtime-openapi-verification.md).
Общий e2e имеет известный environment blocker; применимость и маршрут проверки
описаны в [backend testing](.ai/conventions/backend-testing.md).
Generated Prisma client не редактируется вручную. Credentials, авторизация
доступа к счетам и внешние интеграции остаются server-side.
Перед Node-командами активировать `.nvmrc` через `nvm use --silent`; это выбирает
Node 22. Не запускать отдельную проверку версии, кроме диагностики runtime issue.
После lockfile install единственный completion gate:
```bash
npm ci
npm run verify
```
Gate non-mutating и последовательно проверяет ESLint, application/test TypeScript, risk-based Jest suite и build. `npm run lint:fix` и `npm run format` изменяют files и выполняются только явно; `npm run test:cov` — диагностика без общего threshold. `npm run test:e2e` не входит в passing gate до WL-P2-05 trigger из remediation registry. `npm run test:openapi-contract` — отдельный contract gate для изменений обеих сторон API contract.
Актуальные scripts проверять в `package.json`/`npm run`.
## Enforceable boundaries
- Style задают `eslint.config.mjs` и TypeScript configs; zero-warning lint и оба typecheck обязательны.
- Публичный HTTP API версионируется как `/api/v1`; generated backend OpenAPI — contract authority для frontend snapshot.
- Frontend не вызывает T-Bank напрямую; credentials, rate limiting и integration resilience остаются server-side.
- Не размещать frontend code, generated client или durable documentation в backend.
- Security, money, data integrity, concurrency, public contracts, critical journeys и regressions требуют risk-based tests. DTO, declarative config и thin scripts не требуют отдельного unit test без собственной logic.
Непроверяемые предпочтения являются guidance и не заменяют canonical gate.
ESLint/TypeScript не доказывают правильность domain boundaries, security или
денежных вычислений: для них нужны semantic review и risk-based tests.
+12 -5
View File
@@ -10,6 +10,7 @@ NestJS API WealthLedger. Backend владеет auth, SQLite-данными и s
```bash
cp .env.example .env
nvm use --silent
npm ci
npm run start:dev
```
@@ -27,7 +28,7 @@ T-Bank token в `.env`: он вводится пользователем и хр
npm run verify
```
Он сначала отклоняет runtime вне Node 22, затем запускает hygiene, lint,
Он запускает zero-warning lint,
application/test typechecks, Jest и build. `npm run lint:fix` — отдельная
записывающая команда, `npm run test:cov` — диагностика без общего числового
threshold. `npm run test:e2e` пока не является passing gate: Prisma schema
@@ -36,19 +37,25 @@ engine останавливает migration до запуска Jest. Точны
## Документация
- [Router conventions](.ai/CONVENTIONS.md) и [источники](.ai/SOURCES.md)
- [Backend architecture](.ai/conventions/backend-architecture.md)
- [HTTP API](.ai/conventions/backend-http-api.md), [data](.ai/conventions/backend-data.md) и [testing](.ai/conventions/backend-testing.md)
- [Security/auth](.ai/conventions/security-auth.md) и [integrations](.ai/conventions/backend-integrations.md)
- [Backend workflow](../docs/development/backend-workflow.md)
- [Engineering governance и risk-based testing](../docs/development/engineering-governance.md)
- [OpenSpec authority и lifecycle](../docs/development/openspec-authority-and-lifecycle.md)
- [Карта проектных фактов](../docs/AI_INDEX.md)
- [API contract и runtime OpenAPI](../docs/development/runtime-openapi-verification.md)
- [Обзор поставленной системы](../docs/architecture/system-overview.md)
- [Архитектурные решения](../docs/adr/README.md)
- [Remediation registry](../docs/development/audit-remediation.md)
Из workspace runtime OpenAPI можно read-only сверить с frontend snapshot:
Из backend runtime OpenAPI можно read-only сверить с frontend snapshot:
```bash
npm run test:openapi-contract
```
Полный multi-repository change проверяется из workspace через
native OpenSpec validation and the canonical gates of each affected repository.
Разработка идёт напрямую по задаче пользователя. Каждый affected repo проходит
свой verify; при изменении wire contract дополнительно выполняется contract check.
Обязательного OpenSpec/SDD lifecycle нет.