diff --git a/docs/superpowers/specs/2026-06-14-quality-gate-contract-docs-design.md b/docs/superpowers/specs/2026-06-14-quality-gate-contract-docs-design.md index a93bd1f..97c01e8 100644 --- a/docs/superpowers/specs/2026-06-14-quality-gate-contract-docs-design.md +++ b/docs/superpowers/specs/2026-06-14-quality-gate-contract-docs-design.md @@ -1,92 +1,94 @@ -# Quality Gate, API Contract, and Documentation Refresh Design +# Дизайн стабилизации quality gate, API-контракта и документации -## Status +## Статус -Approved for specification on 2026-06-14. +Одобрено для спецификации 2026-06-14. ## PRD -### Problem +### Проблема -MoexVibe has accumulated coordination debt between tests, generated contracts, and documentation. -The application still builds, but the default backend quality gate is not trustworthy: +В MoexVibe накопился координационный технический долг между тестами, сгенерированными контрактами +и документацией. Приложение всё ещё собирается, но стандартный backend quality gate сейчас нельзя +считать надёжным: -- `npm run lint` fails on an unused variable in a backend test. -- `npm run test:backend` fails because some backend tests call the live MOEX API through the real - `MoexClientService`. -- The live MOEX failures surface as Vitest `DataCloneError` reports because Axios errors contain - non-cloneable request configuration functions. -- `docs/openapi/openapi.yaml` and `apps/frontend/src/api/types.ts` do not include current `auth`, - `portfolios`, and `securities/screener` endpoints. -- README, AGENTS, and Docusaurus pages describe an older state of the repo in several places. -- `npm run build:docs` succeeds but emits Docusaurus broken-link warnings for `/`. +- `npm run lint` падает из-за неиспользуемой переменной в backend-тесте. +- `npm run test:backend` падает, потому что часть backend-тестов обращается к живому MOEX API через + настоящий `MoexClientService`. +- Ошибки live MOEX-запросов проявляются как Vitest `DataCloneError`, потому что `AxiosError` + содержит функции в конфигурации запроса, которые нельзя клонировать между worker'ами. +- `docs/openapi/openapi.yaml` и `apps/frontend/src/api/types.ts` не содержат актуальные эндпоинты + `auth`, `portfolios` и `securities/screener`. +- README, AGENTS и страницы Docusaurus местами описывают старое состояние репозитория. +- `npm run build:docs` успешно генерирует статические файлы, но выводит предупреждения Docusaurus о + broken link на `/`. -This makes future feature work slower: contributors cannot tell which command output matters, which -API contract is current, or whether backend tests are failing because of application behavior or a -network dependency. +Из-за этого следующие задачи делать медленнее: разработчику неочевидно, какой вывод команд важен, +какой API-контракт актуален и почему backend-тесты падают: из-за поведения приложения или из-за +сетевой зависимости. -### Goals +### Цели -1. Make default quality checks deterministic and suitable for local development and CI. -2. Keep live MOEX checks available, but move them out of the default unit-test path. -3. Re-establish a clear source of truth for the OpenAPI contract. -4. Regenerate or synchronize frontend OpenAPI types with the current backend routes. -5. Bring README, AGENTS, and Docusaurus docs in line with the current repo. -6. Remove actionable Docusaurus broken-link warnings from the docs build. +1. Сделать стандартные проверки детерминированными и пригодными для локальной разработки и CI. +2. Сохранить live MOEX-проверки, но вынести их из стандартного unit-test пути. +3. Вернуть понятный источник правды для OpenAPI-контракта. +4. Перегенерировать или синхронизировать frontend OpenAPI-типы с текущими backend routes. +5. Привести README, AGENTS и Docusaurus-документацию к текущему состоянию репозитория. +6. Убрать actionable предупреждения Docusaurus о broken links из `npm run build:docs`. -### Non-Goals +### Не входит в задачу -- No user-facing feature changes. -- No deep refactor of `PortfolioService` or `MoexClientService`. -- No migration to a fully generated frontend API client. -- No Redis, database, auth model, or portfolio analytics redesign. -- No CI provider migration. +- Пользовательские feature-изменения. +- Глубокий рефакторинг `PortfolioService` или `MoexClientService`. +- Переход frontend API-клиента на полностью сгенерированный клиент. +- Redis, изменение схемы БД, redesign auth-модели или portfolio analytics. +- Миграция CI-провайдера. -## Current Findings +## Текущие находки -### Passing Checks +### Проходящие проверки -- `npm run test:frontend` passes: 22 files, 95 tests. -- `npm run format:check` passes. -- `npm run build:backend` passes. -- `npm run build:frontend` passes. -- `npm run build:docs` produces static files successfully. +- `npm run test:frontend` проходит: 22 файла, 95 тестов. +- `npm run format:check` проходит. +- `npm run build:backend` проходит. +- `npm run build:frontend` проходит. +- `npm run build:docs` успешно генерирует статические файлы. -### Failing or Noisy Checks +### Падающие или шумные проверки -- `npm run lint` fails in - `apps/backend/src/modules/securities/screener.service.spec.ts` because `moexClient` is assigned - but not used. -- `npm run test:backend` fails with unhandled Vitest errors. The affected specs instantiate the real - `MoexClientService` and call MOEX: +- `npm run lint` падает в `apps/backend/src/modules/securities/screener.service.spec.ts`, потому + что `moexClient` присваивается, но не используется. +- `npm run test:backend` падает с unhandled Vitest errors. Затронутые specs создают настоящий + `MoexClientService` и ходят в MOEX: - `apps/backend/src/modules/moex-client/moex-client.service.spec.ts` - `apps/backend/src/modules/securities/securities.service.spec.ts` - `apps/backend/src/modules/candles/candles.service.spec.ts` - - similar service specs for shares and bonds also rely on live MOEX access. -- `npm run build:docs` warns that many pages link to `/`. + - похожие service specs для shares и bonds тоже зависят от live MOEX-доступа. +- `npm run build:docs` предупреждает, что многие страницы ссылаются на `/`. -### Stale Documentation and Contract Artifacts +### Устаревшая документация и контрактные артефакты -- `AGENTS.md` says `npm run lint` is backend-only and frontend tests are absent. -- `apps/docs/docs/development/testing.md` says frontend tests are absent. -- `apps/docs/docs/development/commands.md` omits `test:frontend`, docs scripts, and frontend lint. -- `apps/docs/docs/frontend/routes.md` omits `/login`, `/register`, `/profile`, `/portfolios`, - `/portfolios/:id`, and `/screener`. -- `apps/docs/docs/frontend/overview.md` omits auth, portfolio, screener, and test directories. -- `apps/docs/docs/backend/api.md` omits portfolio and screener endpoints. -- `apps/docs/docs/backend/portfolio.md` documents `PATCH /api/v1/portfolios/:id/patch`, while the - controller implements `PATCH /api/v1/portfolios/:id`. -- `docs/openapi/openapi.yaml` and `apps/frontend/src/api/types.ts` only contain the early health, - search, shares, bonds, and candles paths. +- `AGENTS.md` говорит, что `npm run lint` проверяет только backend, а frontend-тестов нет. +- `apps/docs/docs/development/testing.md` говорит, что frontend-тестов нет. +- `apps/docs/docs/development/commands.md` не описывает `test:frontend`, docs scripts и frontend + lint. +- `apps/docs/docs/frontend/routes.md` не описывает `/login`, `/register`, `/profile`, + `/portfolios`, `/portfolios/:id` и `/screener`. +- `apps/docs/docs/frontend/overview.md` не описывает auth, portfolio, screener и test-директории. +- `apps/docs/docs/backend/api.md` не содержит portfolio и screener endpoints. +- `apps/docs/docs/backend/portfolio.md` документирует `PATCH /api/v1/portfolios/:id/patch`, хотя + controller реализует `PATCH /api/v1/portfolios/:id`. +- `docs/openapi/openapi.yaml` и `apps/frontend/src/api/types.ts` содержат только ранние paths для + health, search, shares, bonds и candles. -## Domain Model +## Доменная модель ### Quality Gate -A quality gate is a repeatable command that developers can run without external dependencies unless -the command explicitly says otherwise. +Quality gate: это повторяемая команда, которую разработчик может запускать без внешних зависимостей, +если сама команда явно не говорит обратного. -Default gates: +Стандартные проверки: - `npm run lint` - `npm run test:backend` @@ -96,106 +98,108 @@ Default gates: - `npm run build:docs` - `npm run format:check` -Opt-in gates: +Opt-in проверки: -- Live MOEX integration checks. These may use network access and should not run in default unit-test - or CI jobs unless explicitly requested. +- Live MOEX integration checks. Они могут требовать network access и не должны запускаться в + стандартных unit tests или CI jobs без явного запроса. -### Contract Source +### Источник API-контракта -The authoritative backend contract is the NestJS Swagger document generated from controllers and DTO -decorators at `/api/docs-json`. +Авторитетный backend contract: NestJS Swagger document, который генерируется из controllers и DTO +decorators по `/api/docs-json`. -Generated or synchronized artifacts: +Сгенерированные или синхронизированные артефакты: -- `docs/openapi/openapi.yaml` as a checked-in human-readable snapshot. -- `apps/frontend/src/api/types.ts` as generated TypeScript path and schema types. -- Docusaurus API pages as explanatory documentation, not the canonical machine contract. +- `docs/openapi/openapi.yaml`: checked-in человекочитаемый snapshot. +- `apps/frontend/src/api/types.ts`: сгенерированные TypeScript path и schema types. +- Docusaurus API pages: поясняющая документация, но не канонический machine contract. -### Documentation Source +### Источник документации -Documentation should describe the current repository state, not a historical implementation plan. -Superpowers specs and plans remain project history; README, AGENTS, and Docusaurus pages are the -current onboarding surface. +Документация должна описывать текущее состояние репозитория, а не исторический план реализации. +Superpowers specs и plans остаются историей проекта. README, AGENTS и Docusaurus pages являются +актуальной onboarding-поверхностью. ## ADR -### Decision +### Решение -Split backend tests into deterministic unit tests and opt-in live MOEX integration tests. +Разделить backend tests на детерминированные unit tests и opt-in live MOEX integration tests. -### Rationale +### Обоснование -The default backend test command currently mixes unit behavior with external network availability. -That makes failures ambiguous and produces noisy Vitest serialization errors when Axios rejects with -non-cloneable configuration fields. Unit tests should verify application logic with controlled -fixtures. Live MOEX tests are still valuable, but they belong in a separately named command with an -explicit environment requirement. +Стандартная backend test command сейчас смешивает unit-поведение и доступность внешней сети. Это +делает failures неоднозначными и порождает шумные Vitest serialization errors, когда Axios +возвращает rejection с non-cloneable configuration fields. Unit tests должны проверять логику +приложения на контролируемых fixtures. Live MOEX tests полезны, но должны быть отдельной явно +названной командой с понятным требованием к окружению. -### Consequences +### Последствия -- `npm run test:backend` becomes stable offline. -- Live MOEX coverage remains available through a dedicated integration command. -- Some existing specs will change from "real MOEX smoke test" to "service behavior with mocked +- `npm run test:backend` становится стабильной offline-командой. +- Live MOEX coverage остаётся доступным через отдельную integration command. +- Часть существующих specs изменится с "real MOEX smoke test" на "service behavior with mocked `MoexClientService`". -- Contract drift becomes visible because OpenAPI snapshots and frontend generated types are updated - as part of this work. +- Contract drift станет видимым, потому что OpenAPI snapshots и frontend generated types будут + обновлены в рамках этой работы. ## Backend Architecture -### Unit Test Boundary +### Граница unit tests -Service tests for `SharesService`, `BondsService`, `CandlesService`, and `SecuritiesService` should -mock `MoexClientService` and `CacheService`. +Service tests для `SharesService`, `BondsService`, `CandlesService` и `SecuritiesService` должны +mock'ать `MoexClientService` и `CacheService`. -The mocked data should exercise behavior that matters to MoexVibe: +Mocked data должны проверять поведение, важное для MoexVibe: -- normalized share data is returned from cached or fetched MOEX client data; -- normalized bond data handles missing market fields as nullable values; -- candles are mapped into the public response shape; -- search and screener behavior uses deterministic fixture rows; -- cache metadata remains represented through `{ data, meta }` where service contracts require it. +- нормализованные share data возвращаются из cached или fetched MOEX client data; +- нормализованные bond data корректно обрабатывают отсутствующие market fields как nullable values; +- candles мапятся в public response shape; +- search и screener используют детерминированные fixture rows; +- cache metadata остаётся представленной через `{ data, meta }`, где этого требуют service + contracts. -### Live Integration Test Boundary +### Граница live integration tests -Live MOEX checks should be isolated in `*.integration.spec.ts` files or an equivalent explicit test -path. They should only run through a dedicated command such as `npm run test:integration -w -apps/backend` and should document that network access is required. +Live MOEX checks должны быть изолированы в `*.integration.spec.ts` files или эквивалентном явном +test path. Они должны запускаться только через отдельную команду, например +`npm run test:integration -w apps/backend`, и должны документировать, что для них требуется network +access. -The integration command should not be part of the default `npm run test:backend` command. +Integration command не должна входить в стандартный `npm run test:backend`. -### OpenAPI Decorators +### OpenAPI decorators -Controllers that already exist should expose enough Swagger metadata for generated path types: +Существующие controllers должны отдавать достаточно Swagger metadata для generated path types: - Auth routes: register, login, refresh, logout, me, profile update. -- Securities routes: search and screener. +- Securities routes: search и screener. - Portfolio routes: list, create, detail, update, delete, position mutations, analytics. -- Existing shares, bonds, candles, and health routes. +- Существующие shares, bonds, candles и health routes. -The implementation should prefer existing DTOs and response DTOs. If a response lacks a DTO and -adding one would be large, the first pass may use minimal response decorators while keeping runtime -behavior unchanged. +В реализации нужно предпочитать существующие DTO и response DTO. Если для response нет DTO и +добавление полного DTO слишком раздувает первый проход, можно использовать минимальные response +decorators без изменения runtime behavior. ## Frontend Architecture ### Generated Types -`apps/frontend/src/api/types.ts` should be regenerated from the current backend Swagger JSON after -backend Swagger metadata includes the current routes. +`apps/frontend/src/api/types.ts` должен быть перегенерирован из текущего backend Swagger JSON после +того, как backend Swagger metadata будет покрывать актуальные routes. -The existing hand-written `responses.ts` and API wrapper modules remain in place for this epic. The -goal is contract freshness, not a full client rewrite. +Существующие hand-written `responses.ts` и API wrapper modules остаются на месте в этом эпике. Цель: +свежесть контракта, а не полный rewrite клиента. ### Tests -Frontend tests already exist and pass. This epic should not rewrite frontend testing architecture. -If API response types change, update frontend tests only where the regenerated contract reveals a -real mismatch. +Frontend tests уже существуют и проходят. Этот эпик не должен переписывать frontend testing +architecture. Если API response types изменятся, frontend tests нужно обновлять только там, где +перегенерированный contract выявит реальное несоответствие. ## OpenAPI Contract Scope -The synchronized contract should include at least these paths under `/api/v1`: +Синхронизированный contract должен включать минимум эти paths под `/api/v1`: - `GET /health` - `POST /auth/register` @@ -229,21 +233,22 @@ The synchronized contract should include at least these paths under `/api/v1`: ### README -Update README so quickstart and test sections mention backend, frontend, and docs workspaces. +Обновить README так, чтобы quickstart и test sections упоминали backend, frontend и docs +workspaces. ### AGENTS -Update AGENTS to reflect: +Обновить AGENTS: -- `apps/docs` is part of the workspace. -- `npm run lint` runs backend and frontend lint. -- frontend tests exist. -- pre-commit checks exist through Husky and lint-staged. -- CI exists under `.gitea/workflows/ci.yml`. +- `apps/docs` является частью workspace. +- `npm run lint` запускает backend и frontend lint. +- frontend tests существуют. +- pre-commit checks существуют через Husky и lint-staged. +- CI существует в `.gitea/workflows/ci.yml`. ### Docusaurus -Update current onboarding pages: +Обновить текущие onboarding pages: - development commands; - testing; @@ -254,36 +259,36 @@ Update current onboarding pages: - backend API; - backend portfolio. -Fix Docusaurus config or docs links so `npm run build:docs` no longer reports broken links to `/`. -The separate update-check warning about `/Users/ksv741/.config` permissions is external to the repo -and does not need to be fixed in this epic. +Исправить Docusaurus config или docs links так, чтобы `npm run build:docs` больше не сообщал о +broken links на `/`. Отдельное update-check warning про permissions в `/Users/ksv741/.config` +является внешним к репозиторию и в этот эпик не входит. -## Implementation Phases +## Этапы реализации -### Phase 1: Stabilize Default Checks +### Этап 1: стабилизировать стандартные проверки -1. Fix the unused backend test variable that breaks lint. -2. Convert default backend service specs away from live MOEX calls. -3. Move or add live MOEX smoke coverage under an opt-in integration command. -4. Verify `npm run lint` and `npm run test:backend`. +1. Исправить неиспользуемую backend test variable, которая ломает lint. +2. Перевести стандартные backend service specs с live MOEX calls на mocked dependencies. +3. Вынести или добавить live MOEX smoke coverage под opt-in integration command. +4. Проверить `npm run lint` и `npm run test:backend`. -### Phase 2: Refresh Contract Artifacts +### Этап 2: обновить contract artifacts -1. Add or complete Swagger metadata for current routes. -2. Regenerate `apps/frontend/src/api/types.ts`. -3. Synchronize `docs/openapi/openapi.yaml` with the current contract. -4. Verify generated paths include auth, screener, and portfolio routes. +1. Добавить или завершить Swagger metadata для актуальных routes. +2. Перегенерировать `apps/frontend/src/api/types.ts`. +3. Синхронизировать `docs/openapi/openapi.yaml` с текущим contract. +4. Проверить, что generated paths включают auth, screener и portfolio routes. -### Phase 3: Refresh Documentation +### Этап 3: обновить документацию -1. Update README and AGENTS. -2. Update Docusaurus development, frontend, backend, and portfolio pages. -3. Fix Docusaurus broken `/` link warning. -4. Verify `npm run build:docs`. +1. Обновить README и AGENTS. +2. Обновить Docusaurus development, frontend, backend и portfolio pages. +3. Исправить Docusaurus broken `/` link warning. +4. Проверить `npm run build:docs`. -### Phase 4: Full Verification +### Этап 4: полная проверка -Run: +Запустить: ```bash npm run lint @@ -297,30 +302,32 @@ npm run format:check ## Acceptance Criteria -- `npm run lint` exits 0. -- `npm run test:backend` exits 0 without live MOEX/network dependency. -- `npm run test:frontend` exits 0. -- `npm run build:backend` exits 0. -- `npm run build:frontend` exits 0. -- `npm run build:docs` exits 0 and no longer reports Docusaurus broken links for `/`. -- `npm run format:check` exits 0. -- `apps/frontend/src/api/types.ts` includes current auth, screener, and portfolio paths. -- `docs/openapi/openapi.yaml` includes current auth, screener, and portfolio paths. -- README, AGENTS, and Docusaurus docs no longer claim that frontend tests are absent. -- Portfolio docs use `PATCH /api/v1/portfolios/:id`, matching the controller. +- `npm run lint` завершается с exit code 0. +- `npm run test:backend` завершается с exit code 0 без live MOEX/network dependency. +- `npm run test:frontend` завершается с exit code 0. +- `npm run build:backend` завершается с exit code 0. +- `npm run build:frontend` завершается с exit code 0. +- `npm run build:docs` завершается с exit code 0 и больше не сообщает о Docusaurus broken links на + `/`. +- `npm run format:check` завершается с exit code 0. +- `apps/frontend/src/api/types.ts` содержит актуальные auth, screener и portfolio paths. +- `docs/openapi/openapi.yaml` содержит актуальные auth, screener и portfolio paths. +- README, AGENTS и Docusaurus docs больше не утверждают, что frontend tests отсутствуют. +- Portfolio docs используют `PATCH /api/v1/portfolios/:id`, что соответствует controller. -## Risks +## Риски -- Swagger decorators may expose DTO gaps that were previously hidden by hand-written frontend types. - Keep the first pass focused on path and schema freshness; defer API-client redesign. -- Live MOEX tests may still fail under network restrictions. That is acceptable only for the - opt-in integration command, not for the default backend test command. -- Regenerating OpenAPI artifacts can produce broad diffs. Review generated changes separately from - hand-written docs changes. +- Swagger decorators могут показать DTO gaps, которые раньше были скрыты hand-written frontend + types. Первый проход должен оставаться сфокусированным на свежести paths и schemas; redesign + API-клиента откладывается. +- Live MOEX tests могут по-прежнему падать в окружениях с network restrictions. Это допустимо только + для opt-in integration command, но не для default backend test command. +- Regenerating OpenAPI artifacts может дать большой diff. Generated changes нужно ревьюить отдельно + от hand-written docs changes. -## Spec Self-Review +## Self-Review спецификации -- Placeholder scan: no placeholder markers or incomplete sections. -- Internal consistency: default tests are offline; live MOEX checks are opt-in. -- Scope check: focused on quality gates, contract snapshots, and docs freshness. -- Ambiguity check: acceptance criteria name concrete commands and contract paths. +- Placeholder scan: placeholder markers и незавершённые sections отсутствуют. +- Internal consistency: default tests остаются offline, live MOEX checks являются opt-in. +- Scope check: scope ограничен quality gates, contract snapshots и актуальностью docs. +- Ambiguity check: acceptance criteria называют конкретные commands и contract paths.