docs(specs): archive local verification remediation
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
openspec validate <change> --type change --store wealth-ledger-specs
|
||||
openspec instructions apply --change <change> --json --store wealth-ledger-specs
|
||||
bash scripts/verify-openspec-authority.sh
|
||||
bash scripts/verify-runtime-openapi.sh
|
||||
```
|
||||
|
||||
## Границы
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
schema: spec-driven
|
||||
created: 2026-08-23
|
||||
goal: Закрыть WL-P1-05 и WL-P2-06, WL-P2-09, WL-P2-11; явно отложить WL-P2-05 и WL-P2-13 до стабильного Prisma migration gate.
|
||||
@@ -0,0 +1,5 @@
|
||||
# make-local-verification-agent-safe
|
||||
|
||||
Сделать конкретные локальные backend и runtime OpenAPI проверки
|
||||
детерминированными и безопасными для агента; не маскировать заблокированный
|
||||
Prisma e2e migration gate общим verdict.
|
||||
@@ -0,0 +1,103 @@
|
||||
## Context
|
||||
|
||||
Корень workspace — coordination directory, а не package-manager workspace и не
|
||||
Git-репозиторий. `frontend`, `backend`, `specs` и `docs` — независимые Git
|
||||
roots. `specs/scripts/verify-openspec-authority.sh` уже показывает правильное
|
||||
место для versioned cross-repository tooling: script определяет workspace
|
||||
относительно себя, а не доверяет текущей директории.
|
||||
|
||||
Frontend закрепляет Node 22 и имеет offline `npm run verify`. Для backend нет
|
||||
эквивалентных runtime policy и отдельных quality gates: `lint` запускает ESLint
|
||||
с `--fix`, base tsconfig включает тесты, а build tsconfig исключает их.
|
||||
Существующие e2e tests создают in-memory Nest application, но их script сначала
|
||||
запускает Prisma на фиксированной `/tmp` DB и сейчас падает до Jest. Swagger
|
||||
доступен по `/api/docs-json` при `NODE_ENV=development`;
|
||||
`frontend/scripts/openapi:sync.mjs` — единственный намеренный writer committed
|
||||
snapshot и generated types.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Цели:**
|
||||
|
||||
- Восстановить исполнимые backend gates для lint и TypeScript приложения и
|
||||
тестов с понятной диагностикой failures.
|
||||
- Доказать отдельной read-only командой, что полный runtime OpenAPI document
|
||||
совпадает с committed frontend snapshot.
|
||||
- Сделать Node/npm bootstrap и конкретные verification commands discoverable
|
||||
для агентов.
|
||||
- Сохранить pre-existing working-tree state пользователя; не создавать
|
||||
versioned writes во время проверок.
|
||||
|
||||
**Не входит в scope:**
|
||||
|
||||
- CI/CD, remote verification, Docker, новый task runner, package-manager
|
||||
workspace или общий workspace verifier.
|
||||
- Изоляция backend e2e DB, обновление Prisma packages и восстановление
|
||||
migration gate: WL-P2-05 и зависимый WL-P2-13 отложены из-за нестабильности
|
||||
Prisma 7.9.1 на Node 22.
|
||||
- JSON report format, cached results, plug-in architecture, arbitrary diff
|
||||
bases, сохранённые hashes либо fixture/unit-test harness для process scripts.
|
||||
- Изменение OpenAPI sync writer, generated frontend types, пользовательских API
|
||||
statuses, T-Bank integration, application features, перенос business glossary
|
||||
`CONTEXT.md` или удаление существующего OpenSpec authority fixture test.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Backend check и fix — разные операции
|
||||
|
||||
Backend предоставляет `lint:check` без `--fix`, `lint:fix` с явным write intent
|
||||
и сохраняет `lint` как совместимый alias для `lint:check`. Он предоставляет
|
||||
отдельные команды `typecheck:app` и `typecheck:test` на основе выделенных
|
||||
tsconfig files. Эти команды не создают repository artifacts; build и e2e не
|
||||
объявляются частью нового общего verdict.
|
||||
|
||||
### Runtime OpenAPI сверяется отдельной read-only командой
|
||||
|
||||
`specs/scripts/verify-runtime-openapi.sh` определяет workspace из собственного
|
||||
пути, поднимает backend с детерминированным development environment, ждёт local
|
||||
readiness, получает `/api/docs-json` и всегда завершает child process. Команда
|
||||
рекурсивно упорядочивает JSON object keys, сохраняя array order, записывает
|
||||
canonical runtime и snapshot documents во временную директорию, сравнивает их
|
||||
SHA-256 и выводит unified diff при различии.
|
||||
|
||||
Она никогда не вызывает `openapi:sync`, не записывает frontend snapshot и не
|
||||
обновляет generated types. Это узкий контрактный gate, а не orchestration
|
||||
interface для всех репозиториев.
|
||||
|
||||
### Bootstrap и документация задают один contract
|
||||
|
||||
Оба приложения требуют Node 22 и `npm ci`. Scoped `AGENTS.md` перечисляют
|
||||
только реально существующие non-mutating verification commands и явно отделяют
|
||||
писатели `lint:fix` и `openapi:sync`. Durable development documentation
|
||||
показывает prerequisites и границы отдельной runtime-contract команды.
|
||||
|
||||
### Prisma findings явно отложены, а не маскируются обходом
|
||||
|
||||
`prisma migrate deploy` 7.9.1 воспроизводимо возвращает пустой schema-engine
|
||||
error как на фиксированной, так и на уникальной временной SQLite DB; включение
|
||||
engine diagnostics меняет результат. На 2026-08-23 опубликованная latest
|
||||
stable version также 7.9.1. Не вводится второй migration mechanism и не
|
||||
обновляются зависимости без подтверждённого stable candidate. Реестр фиксирует
|
||||
дату, причину и trigger review: следующая stable Prisma должна трижды применить
|
||||
existing migrations к чистой DB на Node 22.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Runtime backend не стартует или child process остаётся после ошибки] →
|
||||
command создаёт cleanup trap до запуска backend и сохраняет фактический exit
|
||||
status.
|
||||
- [Canonicalization скрывает различие контрактов] → нормализуется только
|
||||
порядок object keys; arrays и каждое JSON field остаются частью SHA-256 input.
|
||||
- [Prisma schema engine остаётся недоступным на supported runtime] → WL-P2-05
|
||||
и WL-P2-13 остаются `deferred` до опубликованной stable версии, прошедшей
|
||||
оговорённый reproduction gate.
|
||||
- [Ignored artifacts путаются с versioned writes] → отдельные команды не
|
||||
считают `build` или e2e своим успехом и документируют свои временные ресурсы.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Не нужны migration production data или deployment sequence. Разработчики
|
||||
запускают `npm ci` под Node 22 в каждом application repository, используют
|
||||
`npm run lint:fix` только осознанно и запускают runtime OpenAPI check отдельно
|
||||
из workspace. Rollback — обычный revert independently versioned changes; он не
|
||||
меняет committed API artifacts или persistent user data.
|
||||
@@ -0,0 +1,42 @@
|
||||
## Why
|
||||
|
||||
В workspace четыре независимых Git-репозитория, но нет единого
|
||||
воспроизводимого verdict локальной проверки. Backend `lint` изменяет исходные
|
||||
файлы, backend e2e останавливается на настройке Prisma до Jest, для тестового
|
||||
TypeScript нет исполнимого gate, а committed frontend OpenAPI snapshot не
|
||||
сверяется с runtime-документом. Из-за этого агент может получить зелёный либо
|
||||
неполный результат, не узнав об изменении исходников или дрейфе
|
||||
межрепозиторного контракта.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Разделить backend lint-проверку и явный auto-fix, добавить отдельные
|
||||
TypeScript gates для приложения и тестов.
|
||||
- Добавить сверку runtime OpenAPI с committed snapshot: она поднимает
|
||||
детерминированный локальный backend, canonicalize оба полных документа во
|
||||
временном хранилище, сравнивает SHA-256 и показывает diff при дрейфе, не
|
||||
вызывая `openapi:sync`.
|
||||
- Унифицировать local bootstrap frontend и backend на Node 22 и `npm ci`,
|
||||
документировать конкретные gates и обновить scoped agent instructions.
|
||||
- Обновить audit remediation registry: этот change станет единственным active
|
||||
remediation для WL-P1-05, WL-P2-06, WL-P2-09 и WL-P2-11; WL-P2-05 и
|
||||
WL-P2-13 будут явно отложены до стабильного Prisma migration gate.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `project-foundation`: Выравнивает требования к runtime/bootstrap приложений и
|
||||
локальные agent verification instructions frontend и backend.
|
||||
- `api-contract-generation`: Требует явной локальной runtime сверки между
|
||||
backend OpenAPI и committed frontend snapshot.
|
||||
|
||||
## Impact
|
||||
|
||||
Затронуты `backend`, `frontend`, `specs` и `docs`. Изменятся backend package
|
||||
scripts, TypeScript-конфигурации, local runtime metadata и документация;
|
||||
generated frontend snapshot и offline-поведение `npm run verify` не изменятся.
|
||||
Небольшая read-only runtime-contract команда в `specs/scripts` не имеет
|
||||
manifest, сохранённых hashes, кэша, plug-in system или fixture test harness.
|
||||
Не вводятся изменения пользовательского HTTP-контракта, persisted production
|
||||
data, внешний сетевой доступ или T-Bank credentials.
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Frontend не зависит от OpenAPI во время работы
|
||||
Работающий frontend MUST использовать ранее сгенерированные transport types и MUST NOT требовать доступность Swagger или OpenAPI endpoint во время запуска, выполнения пользовательского сценария или локальной проверки актуальности generated artifact. Доступ к backend OpenAPI endpoint разрешён только команде синхронизации snapshot и отдельной read-only runtime-contract command.
|
||||
|
||||
#### Scenario: OpenAPI endpoint недоступен после сборки frontend
|
||||
- **WHEN** frontend уже собран, а endpoint OpenAPI временно недоступен
|
||||
- **THEN** frontend продолжает использовать сгенерированный код для запросов к API
|
||||
|
||||
#### Scenario: Локальная проверка выполняется без backend
|
||||
- **WHEN** разработчик запускает локальный frontend quality gate
|
||||
- **THEN** проверка актуальности generated artifact использует сохранённый snapshot и не обращается к OpenAPI endpoint
|
||||
|
||||
#### Scenario: Разработчик проверяет runtime-контракт
|
||||
- **WHEN** разработчик запускает отдельную runtime-contract command
|
||||
- **THEN** command может поднять изолированный local backend только для сравнения
|
||||
его полного runtime OpenAPI document с committed frontend snapshot и не
|
||||
обновляет ни один из артефактов
|
||||
|
||||
### Requirement: Отдельная команда обнаруживает runtime drift OpenAPI
|
||||
Runtime-contract command MUST получать полный OpenAPI document от изолированного
|
||||
local backend по `/api/docs-json`, рекурсивно canonicalize порядок JSON object
|
||||
keys, сохраняя каждое field и array order, и сравнивать его SHA-256 с canonical
|
||||
committed frontend snapshot. При любом content mismatch она MUST завершаться
|
||||
ошибкой и показывать diff. Verification MUST NOT вызывать snapshot
|
||||
synchronization command или записывать committed OpenAPI либо generated type
|
||||
artifacts.
|
||||
|
||||
#### Scenario: Runtime success status отличается от committed snapshot
|
||||
- **WHEN** backend runtime OpenAPI описывает response status, отличный от
|
||||
committed frontend snapshot
|
||||
- **THEN** runtime-contract command завершается ошибкой с diff и оставляет
|
||||
snapshot и generated types без изменений
|
||||
|
||||
#### Scenario: Runtime OpenAPI совпадает с committed snapshot
|
||||
- **WHEN** каждое field canonical runtime document совпадает с canonical
|
||||
committed frontend snapshot
|
||||
- **THEN** runtime-contract stage завершается успешно без изменения frontend files
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Репозитории содержат локальные инструкции для AI-агентов
|
||||
Система SHALL содержать обзорный `AGENTS.md` в корне рабочей области и отдельный `AGENTS.md` в репозиториях frontend, backend, specs и docs. Инструкции MUST описывать назначение области, локальные команды проверки, границы ответственности и связь с SDD-артефактами. Каждая инструкция MUST объявлять `wealth-ledger-specs` и `specs/openspec` как canonical OpenSpec authority, требовать явный `--store wealth-ledger-specs` для поддерживаемых OpenSpec операций и запрещать versioned framework-specific planning artifacts вне canonical store. Локальные инструкции MUST направлять агента к durable remediation registry, когда задача связана с findings технического аудита. Backend instructions MUST различать non-mutating verification commands и явные auto-fix commands.
|
||||
|
||||
#### Scenario: Агент начинает работу в отдельном репозитории
|
||||
- **WHEN** агент открывает любой из репозиториев frontend, backend, specs или docs
|
||||
- **THEN** он может определить локальные правила работы, canonical OpenSpec authority и источник связанных спецификаций из `AGENTS.md` этого репозитория
|
||||
|
||||
#### Scenario: Агент получает audit-derived задачу из отдельного репозитория
|
||||
- **WHEN** задача ссылается на finding технического аудита
|
||||
- **THEN** локальный `AGENTS.md` указывает на remediation registry и canonical OpenSpec workflow с явным store selection до начала изменения кода
|
||||
|
||||
#### Scenario: Агент выбирает backend quality gate
|
||||
- **WHEN** агент читает backend instructions перед локальной проверкой
|
||||
- **THEN** он запускает документированные non-mutating lint/typecheck commands и не получает auto-fix как неявный quality gate
|
||||
|
||||
### Requirement: Локальный план MVP не является источником спецификации
|
||||
Система SHALL содержать корневой локальный `MVP.md` с границами MVP, принятыми решениями, порядком поставок и критериями готовности. Требования к функциональности MUST храниться в OpenSpec, а устойчивая документация MUST храниться в репозитории docs. Frontend и backend MUST требовать Node 22 и документировать lockfile-safe bootstrap через `npm ci`.
|
||||
|
||||
#### Scenario: Решение уточняется во время SDD-изменения
|
||||
- **WHEN** изменение уточняет наблюдаемое поведение системы
|
||||
- **THEN** оно фиксируется в OpenSpec и соответствующей документации, а `MVP.md` остается компактной картой поставки
|
||||
|
||||
#### Scenario: Агент bootstrap-ит application repository
|
||||
- **WHEN** агент начинает локальную проверку frontend или backend на чистой машине
|
||||
- **THEN** он может определить Node 22 и выполнить `npm ci` без угадывания runtime policy
|
||||
@@ -0,0 +1,58 @@
|
||||
## 1. Зафиксировать активный и отложенный audit scope
|
||||
|
||||
- [x] 1.1 Обновить `docs/development/audit-remediation.md`: пометить только
|
||||
WL-P1-05, WL-P2-06, WL-P2-09 и WL-P2-11 как `active` с
|
||||
`make-local-verification-agent-safe`; записать WL-P2-05 и WL-P2-13 как
|
||||
`deferred` с датой, наблюдаемой причиной Prisma 7.9.1 и trigger review;
|
||||
не помечать finding как `done`.
|
||||
- [x] 1.2 Обновить durable development documentation и scoped `AGENTS.md`,
|
||||
чтобы они называли только реально существующие non-mutating gates, Node 22 и
|
||||
`npm ci`, а writers `lint:fix` и `openapi:sync` были явными.
|
||||
|
||||
## 2. Восстановить non-mutating local gates backend
|
||||
|
||||
- [x] 2.1 Добавить backend Node 22 runtime metadata и lockfile-safe bootstrap
|
||||
guidance; разделить `lint:check`, `lint:fix` и совместимый non-mutating alias
|
||||
`lint` в `backend/package.json`.
|
||||
- [x] 2.2 Добавить отдельные backend TypeScript configurations и scripts для
|
||||
приложения и тестов; application compilation исключает test sources, test
|
||||
compilation включает test surface, оба выполняются без repository artifacts.
|
||||
- [x] 2.3 Исправить существующий неполный `TbankClient` test double или
|
||||
заменить его typed test adapter, чтобы test TypeScript gate проходил без
|
||||
ослабления production client contract.
|
||||
- [x] 2.4 Документировать, что текущие `test:e2e` и общий workspace verdict не
|
||||
являются частью этого change, пока WL-P2-05/P2-13 отложены; не добавлять
|
||||
обходной Prisma runner или ложный `npm run verify`.
|
||||
|
||||
## 3. Проверить runtime API contract без writers
|
||||
|
||||
- [x] 3.1 Реализовать минимальный runtime OpenAPI comparison helper, который
|
||||
запускает backend с deterministic development test environment, ждёт local
|
||||
readiness, получает `/api/docs-json`, canonicalizes object keys во временных
|
||||
files, сравнивает SHA-256 полного document, выводит diff при mismatch и всегда
|
||||
завершает child process.
|
||||
- [x] 3.2 Подтвердить, что helper никогда не вызывает
|
||||
`frontend/scripts/openapi-sync.mjs`, не обращается к external service, не
|
||||
требует T-Bank credentials и не записывает committed snapshot/generated
|
||||
types; запустить его для текущего contract и устранить каждый content mismatch
|
||||
через согласованный contract workflow, а не скрытым переписыванием.
|
||||
|
||||
## 4. Completion evidence и audit tracking
|
||||
|
||||
- [x] 4.1 Запустить `openspec validate make-local-verification-agent-safe
|
||||
--type change --strict --store wealth-ledger-specs`, `openspec validate
|
||||
--specs --strict --store wealth-ledger-specs`, authority verifier, отдельный
|
||||
runtime OpenAPI check и relevant per-repository checks; зафиксировать точные
|
||||
commands и observed results.
|
||||
- [x] 4.2 Выполнить independent read-only review diff, OpenSpec deltas,
|
||||
temporary-resource behavior, runtime-contract comparison и точности
|
||||
документации; устранить каждую confirmed issue и подтвердить, что этот task
|
||||
list не содержит post-archive bookkeeping, commit или registry-completion
|
||||
checkbox.
|
||||
- [x] 4.3 Выполнить applicable pre-archive `opsx-verify` procedure и устранить
|
||||
каждый blocking result. Эта задача завершается до archive. Только после
|
||||
завершения каждого checkbox change может быть archived и synced; только после
|
||||
этого remediation registry вне task list получает actual archive path,
|
||||
commits, terminal evidence, четыре статуса `done` и ровно одну следующую
|
||||
рекомендуемую задачу. WL-P2-05 и WL-P2-13 остаются `deferred` до своего
|
||||
отдельного change.
|
||||
@@ -29,16 +29,42 @@ Frontend SHALL хранить в репозитории версионируем
|
||||
- **THEN** команда завершается с ошибкой и существующие snapshot и сгенерированные transport types не изменяются
|
||||
|
||||
### Requirement: Frontend не зависит от OpenAPI во время работы
|
||||
Работающий frontend MUST использовать ранее сгенерированные transport types и MUST NOT требовать доступность Swagger или OpenAPI endpoint во время запуска, выполнения пользовательского сценария или локальной проверки актуальности generated artifact. Доступ к backend OpenAPI endpoint разрешён только команде синхронизации snapshot.
|
||||
Работающий frontend MUST использовать ранее сгенерированные transport types и MUST NOT требовать доступность Swagger или OpenAPI endpoint во время запуска, выполнения пользовательского сценария или локальной проверки актуальности generated artifact. Доступ к backend OpenAPI endpoint разрешён только команде синхронизации snapshot и отдельной read-only runtime-contract command.
|
||||
|
||||
#### Scenario: OpenAPI endpoint недоступен после сборки frontend
|
||||
- **WHEN** frontend уже собран, а endpoint OpenAPI временно недоступен
|
||||
- **THEN** frontend продолжает использовать сгенерированный код для запросов к API
|
||||
|
||||
#### Scenario: Локальная проверка выполняется без backend
|
||||
- **WHEN** разработчик запускает локальный quality gate без доступного backend
|
||||
- **WHEN** разработчик запускает локальный frontend quality gate
|
||||
- **THEN** проверка актуальности generated artifact использует сохранённый snapshot и не обращается к OpenAPI endpoint
|
||||
|
||||
#### Scenario: Разработчик проверяет runtime-контракт
|
||||
- **WHEN** разработчик запускает отдельную runtime-contract command
|
||||
- **THEN** command может поднять изолированный local backend только для сравнения
|
||||
его полного runtime OpenAPI document с committed frontend snapshot и не
|
||||
обновляет ни один из артефактов
|
||||
|
||||
### Requirement: Отдельная команда обнаруживает runtime drift OpenAPI
|
||||
Runtime-contract command MUST получать полный OpenAPI document от изолированного
|
||||
local backend по `/api/docs-json`, рекурсивно canonicalize порядок JSON object
|
||||
keys, сохраняя каждое field и array order, и сравнивать его SHA-256 с canonical
|
||||
committed frontend snapshot. При любом content mismatch она MUST завершаться
|
||||
ошибкой и показывать diff. Verification MUST NOT вызывать snapshot
|
||||
synchronization command или записывать committed OpenAPI либо generated type
|
||||
artifacts.
|
||||
|
||||
#### Scenario: Runtime success status отличается от committed snapshot
|
||||
- **WHEN** backend runtime OpenAPI описывает response status, отличный от
|
||||
committed frontend snapshot
|
||||
- **THEN** runtime-contract command завершается ошибкой с diff и оставляет
|
||||
snapshot и generated types без изменений
|
||||
|
||||
#### Scenario: Runtime OpenAPI совпадает с committed snapshot
|
||||
- **WHEN** каждое field canonical runtime document совпадает с canonical
|
||||
committed frontend snapshot
|
||||
- **THEN** runtime-contract stage завершается успешно без изменения frontend files
|
||||
|
||||
### Requirement: Frontend обнаруживает дрейф сгенерированного контракта
|
||||
Frontend SHALL проверять, что committed generated transport types соответствуют versioned OpenAPI snapshot, и MUST завершать quality gate с ошибкой при расхождении.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
## Requirements
|
||||
|
||||
### Requirement: Репозитории содержат локальные инструкции для AI-агентов
|
||||
Система SHALL содержать обзорный `AGENTS.md` в корне рабочей области и отдельный `AGENTS.md` в репозиториях frontend, backend, specs и docs. Инструкции MUST описывать назначение области, локальные команды проверки, границы ответственности и связь с SDD-артефактами. Каждая инструкция MUST объявлять `wealth-ledger-specs` и `specs/openspec` как canonical OpenSpec authority, требовать явный `--store wealth-ledger-specs` для поддерживаемых OpenSpec операций и запрещать versioned framework-specific planning artifacts вне canonical store. Локальные инструкции MUST направлять агента к durable remediation registry, когда задача связана с findings технического аудита.
|
||||
Система SHALL содержать обзорный `AGENTS.md` в корне рабочей области и отдельный `AGENTS.md` в репозиториях frontend, backend, specs и docs. Инструкции MUST описывать назначение области, локальные команды проверки, границы ответственности и связь с SDD-артефактами. Каждая инструкция MUST объявлять `wealth-ledger-specs` и `specs/openspec` как canonical OpenSpec authority, требовать явный `--store wealth-ledger-specs` для поддерживаемых OpenSpec операций и запрещать versioned framework-specific planning artifacts вне canonical store. Локальные инструкции MUST направлять агента к durable remediation registry, когда задача связана с findings технического аудита. Backend instructions MUST различать non-mutating verification commands и явные auto-fix commands.
|
||||
|
||||
#### Scenario: Агент начинает работу в отдельном репозитории
|
||||
- **WHEN** агент открывает любой из репозиториев frontend, backend, specs или docs
|
||||
@@ -17,13 +17,21 @@
|
||||
- **WHEN** задача ссылается на finding технического аудита
|
||||
- **THEN** локальный `AGENTS.md` указывает на remediation registry и canonical OpenSpec workflow с явным store selection до начала изменения кода
|
||||
|
||||
#### Scenario: Агент выбирает backend quality gate
|
||||
- **WHEN** агент читает backend instructions перед локальной проверкой
|
||||
- **THEN** он запускает документированные non-mutating lint/typecheck commands и не получает auto-fix как неявный quality gate
|
||||
|
||||
### Requirement: Локальный план MVP не является источником спецификации
|
||||
Система SHALL содержать корневой локальный `MVP.md` с границами MVP, принятыми решениями, порядком поставок и критериями готовности. Требования к функциональности MUST храниться в OpenSpec, а устойчивая документация MUST храниться в репозитории docs.
|
||||
Система SHALL содержать корневой локальный `MVP.md` с границами MVP, принятыми решениями, порядком поставок и критериями готовности. Требования к функциональности MUST храниться в OpenSpec, а устойчивая документация MUST храниться в репозитории docs. Frontend и backend MUST требовать Node 22 и документировать lockfile-safe bootstrap через `npm ci`.
|
||||
|
||||
#### Scenario: Решение уточняется во время SDD-изменения
|
||||
- **WHEN** изменение уточняет наблюдаемое поведение системы
|
||||
- **THEN** оно фиксируется в OpenSpec и соответствующей документации, а `MVP.md` остается компактной картой поставки
|
||||
|
||||
#### Scenario: Агент bootstrap-ит application repository
|
||||
- **WHEN** агент начинает локальную проверку frontend или backend на чистой машине
|
||||
- **THEN** он может определить Node 22 и выполнить `npm ci` без угадывания runtime policy
|
||||
|
||||
### Requirement: Frontend предоставляет минимальный каркас приложения
|
||||
Frontend SHALL предоставлять русскоязычный адаптивный каркас приложения с маршрутами `/login`, `/register`, `/accounts`, `/accounts/:accountId` и `/settings`, светлой финансовой темой и навигацией для счетов и настроек. Каркас MUST содержать состояния загрузки, ошибки и пустых данных без реализации доменных сценариев.
|
||||
|
||||
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
workspace_dir="$(cd -- "$script_dir/../.." && pwd)"
|
||||
backend_dir="$workspace_dir/backend"
|
||||
snapshot_path="$workspace_dir/frontend/src/shared/api/spec/openapi.json"
|
||||
|
||||
if [[ ! -f "$snapshot_path" ]]; then
|
||||
echo "Missing committed OpenAPI snapshot: $snapshot_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
node_major="$(node -p "process.versions.node.split('.')[0]")"
|
||||
if [[ "$node_major" != "22" ]]; then
|
||||
echo "Node 22 is required; found $(node --version)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
temporary_dir="$(mktemp -d "${TMPDIR:-/tmp}/wealth-ledger-runtime-openapi.XXXXXX")"
|
||||
backend_pid=""
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "$backend_pid" ]] && kill -0 "$backend_pid" 2>/dev/null; then
|
||||
kill "$backend_pid" 2>/dev/null || true
|
||||
wait "$backend_pid" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$temporary_dir"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
port="$(node -e "const server = require('node:net').createServer(); server.listen(0, '127.0.0.1', () => { console.log(server.address().port); server.close(); });")"
|
||||
runtime_path="$temporary_dir/runtime.json"
|
||||
canonical_runtime_path="$temporary_dir/runtime.canonical.json"
|
||||
canonical_snapshot_path="$temporary_dir/snapshot.canonical.json"
|
||||
backend_log="$temporary_dir/backend.log"
|
||||
compile_log="$temporary_dir/compile.log"
|
||||
|
||||
if ! (
|
||||
cd "$backend_dir"
|
||||
./node_modules/.bin/tsc --project tsconfig.build.json --outDir "$temporary_dir/dist"
|
||||
) >"$compile_log" 2>&1; then
|
||||
echo "Temporary backend compilation failed:" >&2
|
||||
sed -n '1,160p' "$compile_log" >&2
|
||||
exit 1
|
||||
fi
|
||||
ln -s "$backend_dir/node_modules" "$temporary_dir/dist/node_modules"
|
||||
|
||||
(
|
||||
cd "$backend_dir"
|
||||
NODE_ENV=development \
|
||||
PORT="$port" \
|
||||
DATABASE_URL="file:$temporary_dir/runtime.db" \
|
||||
FRONTEND_ORIGIN="http://localhost:5173" \
|
||||
JWT_SECRET="runtime-openapi-check-secret-at-least-32" \
|
||||
TOKEN_ENCRYPTION_KEY="MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=" \
|
||||
TBANK_GRPC_ENDPOINT="127.0.0.1:1" \
|
||||
TBANK_RATE_LIMIT_PER_SECOND=5 \
|
||||
LOG_LEVEL=error \
|
||||
node "$temporary_dir/dist/src/main.js"
|
||||
) >"$backend_log" 2>&1 &
|
||||
backend_pid="$!"
|
||||
|
||||
for _ in $(seq 1 60); do
|
||||
if node -e "fetch(process.argv[1]).then((response) => process.exit(response.ok ? 0 : 1)).catch(() => process.exit(1))" "http://127.0.0.1:$port/api/docs-json"; then
|
||||
break
|
||||
fi
|
||||
if ! kill -0 "$backend_pid" 2>/dev/null; then
|
||||
echo "Backend stopped before exposing /api/docs-json:" >&2
|
||||
sed -n '1,160p' "$backend_log" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.25
|
||||
done
|
||||
|
||||
if ! node -e 'fetch(process.argv[1]).then(async (response) => { if (!response.ok) throw new Error(`${response.status} ${response.statusText}`); require("node:fs").writeFileSync(process.argv[2], await response.text()); }).catch((error) => { console.error(`OpenAPI fetch failed: ${error.message}`); process.exit(1); })' "http://127.0.0.1:$port/api/docs-json" "$runtime_path"; then
|
||||
echo "Backend did not expose /api/docs-json within 15 seconds:" >&2
|
||||
sed -n '1,160p' "$backend_log" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if node - "$runtime_path" "$snapshot_path" "$canonical_runtime_path" "$canonical_snapshot_path" <<'NODE'
|
||||
const { createHash } = require('node:crypto');
|
||||
const { readFileSync, writeFileSync } = require('node:fs');
|
||||
|
||||
function canonicalize(value) {
|
||||
if (Array.isArray(value)) return value.map(canonicalize);
|
||||
if (value && typeof value === 'object') {
|
||||
return Object.fromEntries(
|
||||
Object.keys(value)
|
||||
.sort()
|
||||
.map((key) => [key, canonicalize(value[key])]),
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const [runtimePath, snapshotPath, canonicalRuntimePath, canonicalSnapshotPath] =
|
||||
process.argv.slice(2);
|
||||
const documents = [runtimePath, snapshotPath].map((path) =>
|
||||
canonicalize(JSON.parse(readFileSync(path, 'utf8'))),
|
||||
);
|
||||
const serialized = documents.map((document) => `${JSON.stringify(document, null, 2)}\n`);
|
||||
writeFileSync(canonicalRuntimePath, serialized[0]);
|
||||
writeFileSync(canonicalSnapshotPath, serialized[1]);
|
||||
const hashes = serialized.map((value) =>
|
||||
createHash('sha256').update(value).digest('hex'),
|
||||
);
|
||||
console.log(`runtime SHA-256: ${hashes[0]}`);
|
||||
console.log(`snapshot SHA-256: ${hashes[1]}`);
|
||||
process.exitCode = hashes[0] === hashes[1] ? 0 : 1;
|
||||
NODE
|
||||
then
|
||||
comparison_status=0
|
||||
else
|
||||
comparison_status=$?
|
||||
fi
|
||||
|
||||
if [[ "$comparison_status" -ne 0 ]]; then
|
||||
echo "Runtime OpenAPI differs from the committed frontend snapshot:" >&2
|
||||
diff -u "$canonical_snapshot_path" "$canonical_runtime_path" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Runtime OpenAPI matches the committed frontend snapshot."
|
||||
Reference in New Issue
Block a user