diff --git a/AGENTS.md b/AGENTS.md index c9e917f..08ac781 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,31 +1,28 @@ # Backend -## Назначение +## Назначение и authority -NestJS API WealthLedger. Все функциональные изменения планируются в `../specs/openspec` до реализации. +NestJS API WealthLedger владеет доменной логикой, данными, конфигурацией и server-side интеграциями. Canonical OpenSpec store — `wealth-ledger-specs` в `../specs/openspec`; OpenSpec commands, читающие или изменяющие change, всегда выполняются с `--store wealth-ledger-specs`. Versioned planning artifacts вне canonical store запрещены, включая `superpowers/plans`; `../references/**` read-only. -## Проверка +Перед изменением поведения прочитать [`../docs/architecture/system-overview.md`](../docs/architecture/system-overview.md), для терминов — [`../docs/architecture/domain-glossary.md`](../docs/architecture/domain-glossary.md), для решений — [`../docs/adr/README.md`](../docs/adr/README.md). Testing и обязательная Solution Assessment для auth/security/storage/integrations/tooling определены в [`../docs/development/engineering-governance.md`](../docs/development/engineering-governance.md). Audit-derived работа начинается с [`../docs/development/audit-remediation.md`](../docs/development/audit-remediation.md). + +## Canonical gate + +Node 22 обязателен. После lockfile install единственный completion gate: ```bash npm ci -npm run lint -npm run typecheck:app -npm run typecheck:test +npm run verify ``` -Node 22 обязателен. `npm run lint` и `npm run lint:check` не изменяют -versioned-файлы; `npm run lint:fix` — явная записывающая команда. Текущий -`npm run test:e2e` временно не является надёжным gate: WL-P2-05 отложен из-за -Prisma schema-engine failure; статус и trigger пересмотра указаны в -`../docs/development/audit-remediation.md`. -Для read-only сверки runtime OpenAPI с frontend snapshot под Node 22 запускать -из workspace `bash specs/scripts/verify-runtime-openapi.sh`. +Gate non-mutating и последовательно проверяет runtime, hygiene, 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. -## Границы +## Enforceable boundaries -- Перед изменением поставленного поведения или доменных терминов прочитать [`../docs/architecture/system-overview.md`](../docs/architecture/system-overview.md) и [`../docs/architecture/domain-glossary.md`](../docs/architecture/domain-glossary.md). -- Публичный HTTP API версионируется как `/api/v1`; OpenAPI из backend - источник контракта для frontend. -- Backend отвечает за доменную логику, данные, конфигурацию и внешние интеграции. Frontend не вызывает T-Bank напрямую; будущая интеграция остается за backend boundary. -- Не размещать frontend-код, сгенерированный клиент или постоянную документацию в этом репозитории. -- Для задачи по Audit ID, продолжения remediation или выбора следующего улучшения сначала прочитать [`../docs/development/audit-remediation.md`](../docs/development/audit-remediation.md); единственный canonical OpenSpec store — `wealth-ledger-specs` в `../specs/openspec`, а OpenSpec commands, читающие или изменяющие change, выполнять с `--store wealth-ledger-specs`. -- Не создавать и не коммитить framework-specific planning/spec/task/design artifacts: versioned SDD-артефакты хранятся только в `../specs/openspec`; `../references/**` остаётся read-only исключением. +- 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. diff --git a/README.md b/README.md index 20085ef..995fa21 100644 --- a/README.md +++ b/README.md @@ -21,23 +21,24 @@ T-Bank token в `.env`: он вводится пользователем и хр ## Проверка -Следующие команды не изменяют исходные файлы: +Единый completion gate не изменяет versioned-файлы: ```bash -npm run lint -npm run typecheck:app -npm run typecheck:test -npm test -- --runInBand +npm run verify ``` -`npm run lint:fix` — явная команда автоисправления. `npm run test:e2e` пока не -является passing gate: Prisma schema engine останавливает migration до запуска -Jest. Точный статус и trigger пересмотра находятся в -[remediation registry](../docs/development/audit-remediation.md). +Он сначала отклоняет runtime вне Node 22, затем запускает hygiene, lint, +application/test typechecks, Jest и build. `npm run lint:fix` — отдельная +записывающая команда, `npm run test:cov` — диагностика без общего числового +threshold. `npm run test:e2e` пока не является passing gate: Prisma schema +engine останавливает migration до запуска Jest. Точный статус и trigger +пересмотра находятся в [remediation registry](../docs/development/audit-remediation.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) - [API contract и runtime OpenAPI](../docs/development/runtime-openapi-verification.md) - [Обзор поставленной системы](../docs/architecture/system-overview.md) - [Архитектурные решения](../docs/adr/README.md) @@ -48,3 +49,6 @@ Jest. Точный статус и trigger пересмотра находятс ```bash bash specs/scripts/verify-runtime-openapi.sh ``` + +Полный multi-repository change проверяется из workspace через +`bash specs/scripts/verify-workspace-change.sh `. diff --git a/package.json b/package.json index 6fe94a3..607769f 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "node": "22.x" }, "scripts": { + "runtime:check": "node scripts/verify-node-version.mjs", "build": "prisma generate && nest build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "start": "nest start", @@ -25,7 +26,8 @@ "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "DATABASE_URL=file:/tmp/wealth-ledger-e2e.db prisma migrate deploy && NODE_ENV=development NODE_OPTIONS=--experimental-vm-modules jest --config ./test/jest-e2e.json" + "test:e2e": "DATABASE_URL=file:/tmp/wealth-ledger-e2e.db prisma migrate deploy && NODE_ENV=development NODE_OPTIONS=--experimental-vm-modules jest --config ./test/jest-e2e.json", + "verify": "npm run runtime:check && npm run hygiene:check && npm run lint && npm run typecheck:app && npm run typecheck:test && npm test -- --runInBand && npm run build" }, "dependencies": { "@grpc/grpc-js": "^1.14.4", diff --git a/scripts/verify-node-version.mjs b/scripts/verify-node-version.mjs new file mode 100644 index 0000000..d940889 --- /dev/null +++ b/scripts/verify-node-version.mjs @@ -0,0 +1,26 @@ +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, resolve } from 'node:path'; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const packageJson = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8')); +const nvmVersion = readFileSync(resolve(root, '.nvmrc'), 'utf8').trim(); +const expectedMajor = nvmVersion.replace(/^v/, '').split('.')[0]; +const expectedEngine = `${expectedMajor}.x`; + +if (packageJson.engines?.node !== expectedEngine) { + throw new Error( + `Node runtime contracts disagree: .nvmrc=${nvmVersion}, package.json engines.node=${packageJson.engines?.node ?? 'missing'}`, + ); +} + +const actualVersion = process.versions.node; +const actualMajor = actualVersion.split('.')[0]; +if (actualMajor !== expectedMajor) { + console.error( + `Unsupported Node.js runtime: expected ${expectedEngine}, actual ${actualVersion}. Activate .nvmrc before running repository checks.`, + ); + process.exit(1); +} + +console.log(`Node.js runtime verified: ${actualVersion} (${expectedEngine})`);