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 validate <change> --type change --store wealth-ledger-specs
|
||||||
openspec instructions apply --change <change> --json --store wealth-ledger-specs
|
openspec instructions apply --change <change> --json --store wealth-ledger-specs
|
||||||
bash scripts/verify-openspec-authority.sh
|
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 не изменяются
|
- **THEN** команда завершается с ошибкой и существующие snapshot и сгенерированные transport types не изменяются
|
||||||
|
|
||||||
### Requirement: Frontend не зависит от OpenAPI во время работы
|
### 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
|
#### Scenario: OpenAPI endpoint недоступен после сборки frontend
|
||||||
- **WHEN** frontend уже собран, а endpoint OpenAPI временно недоступен
|
- **WHEN** frontend уже собран, а endpoint OpenAPI временно недоступен
|
||||||
- **THEN** frontend продолжает использовать сгенерированный код для запросов к API
|
- **THEN** frontend продолжает использовать сгенерированный код для запросов к API
|
||||||
|
|
||||||
#### Scenario: Локальная проверка выполняется без backend
|
#### Scenario: Локальная проверка выполняется без backend
|
||||||
- **WHEN** разработчик запускает локальный quality gate без доступного backend
|
- **WHEN** разработчик запускает локальный frontend quality gate
|
||||||
- **THEN** проверка актуальности generated artifact использует сохранённый snapshot и не обращается к OpenAPI endpoint
|
- **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 обнаруживает дрейф сгенерированного контракта
|
### Requirement: Frontend обнаруживает дрейф сгенерированного контракта
|
||||||
Frontend SHALL проверять, что committed generated transport types соответствуют versioned OpenAPI snapshot, и MUST завершать quality gate с ошибкой при расхождении.
|
Frontend SHALL проверять, что committed generated transport types соответствуют versioned OpenAPI snapshot, и MUST завершать quality gate с ошибкой при расхождении.
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Requirement: Репозитории содержат локальные инструкции для AI-агентов
|
### 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: Агент начинает работу в отдельном репозитории
|
#### Scenario: Агент начинает работу в отдельном репозитории
|
||||||
- **WHEN** агент открывает любой из репозиториев frontend, backend, specs или docs
|
- **WHEN** агент открывает любой из репозиториев frontend, backend, specs или docs
|
||||||
@@ -17,13 +17,21 @@
|
|||||||
- **WHEN** задача ссылается на finding технического аудита
|
- **WHEN** задача ссылается на finding технического аудита
|
||||||
- **THEN** локальный `AGENTS.md` указывает на remediation registry и canonical OpenSpec workflow с явным store selection до начала изменения кода
|
- **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 не является источником спецификации
|
### 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-изменения
|
#### Scenario: Решение уточняется во время SDD-изменения
|
||||||
- **WHEN** изменение уточняет наблюдаемое поведение системы
|
- **WHEN** изменение уточняет наблюдаемое поведение системы
|
||||||
- **THEN** оно фиксируется в OpenSpec и соответствующей документации, а `MVP.md` остается компактной картой поставки
|
- **THEN** оно фиксируется в OpenSpec и соответствующей документации, а `MVP.md` остается компактной картой поставки
|
||||||
|
|
||||||
|
#### Scenario: Агент bootstrap-ит application repository
|
||||||
|
- **WHEN** агент начинает локальную проверку frontend или backend на чистой машине
|
||||||
|
- **THEN** он может определить Node 22 и выполнить `npm ci` без угадывания runtime policy
|
||||||
|
|
||||||
### Requirement: Frontend предоставляет минимальный каркас приложения
|
### Requirement: Frontend предоставляет минимальный каркас приложения
|
||||||
Frontend SHALL предоставлять русскоязычный адаптивный каркас приложения с маршрутами `/login`, `/register`, `/accounts`, `/accounts/:accountId` и `/settings`, светлой финансовой темой и навигацией для счетов и настроек. Каркас MUST содержать состояния загрузки, ошибки и пустых данных без реализации доменных сценариев.
|
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