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 new file mode 100644 index 0000000..a93bd1f --- /dev/null +++ b/docs/superpowers/specs/2026-06-14-quality-gate-contract-docs-design.md @@ -0,0 +1,326 @@ +# Quality Gate, API Contract, and Documentation Refresh Design + +## Status + +Approved for specification on 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: + +- `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 `/`. + +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. + +### 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. + +### 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. + +## 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. + +### 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: + - `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 `/`. + +### 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. + +## Domain Model + +### Quality Gate + +A quality gate is a repeatable command that developers can run without external dependencies unless +the command explicitly says otherwise. + +Default gates: + +- `npm run lint` +- `npm run test:backend` +- `npm run test:frontend` +- `npm run build:backend` +- `npm run build:frontend` +- `npm run build:docs` +- `npm run format:check` + +Opt-in gates: + +- Live MOEX integration checks. These may use network access and should not run in default unit-test + or CI jobs unless explicitly requested. + +### Contract Source + +The authoritative backend contract is the NestJS Swagger document generated from controllers and DTO +decorators at `/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. + +### 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. + +## ADR + +### Decision + +Split backend tests into deterministic unit tests and 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. + +### 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 + `MoexClientService`". +- Contract drift becomes visible because OpenAPI snapshots and frontend generated types are updated + as part of this work. + +## Backend Architecture + +### Unit Test Boundary + +Service tests for `SharesService`, `BondsService`, `CandlesService`, and `SecuritiesService` should +mock `MoexClientService` and `CacheService`. + +The mocked data should exercise behavior that matters to 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. + +### Live Integration Test Boundary + +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. + +The integration command should not be part of the default `npm run test:backend` command. + +### OpenAPI Decorators + +Controllers that already exist should expose enough Swagger metadata for generated path types: + +- Auth routes: register, login, refresh, logout, me, profile update. +- Securities routes: search and screener. +- Portfolio routes: list, create, detail, update, delete, position mutations, analytics. +- Existing shares, bonds, candles, and 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. + +## 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. + +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. + +### 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. + +## OpenAPI Contract Scope + +The synchronized contract should include at least these paths under `/api/v1`: + +- `GET /health` +- `POST /auth/register` +- `POST /auth/login` +- `POST /auth/refresh` +- `POST /auth/logout` +- `GET /auth/me` +- `PATCH /auth/me` +- `GET /securities/search` +- `GET /securities/screener` +- `GET /securities/shares/{secid}` +- `GET /securities/shares/{secid}/marketdata` +- `GET /securities/shares/{secid}/dividends` +- `GET /securities/shares/{secid}/history` +- `GET /securities/shares/{secid}/candles` +- `GET /securities/bonds/{secid}` +- `GET /securities/bonds/{secid}/marketdata` +- `GET /securities/bonds/{secid}/history` +- `GET /securities/bonds/{secid}/candles` +- `GET /portfolios` +- `POST /portfolios` +- `GET /portfolios/{id}` +- `PATCH /portfolios/{id}` +- `DELETE /portfolios/{id}` +- `POST /portfolios/{id}/positions` +- `PATCH /portfolios/{id}/positions/{positionId}` +- `DELETE /portfolios/{id}/positions/{positionId}` +- `GET /portfolios/{id}/analytics` + +## Documentation Architecture + +### README + +Update README so quickstart and test sections mention backend, frontend, and docs workspaces. + +### AGENTS + +Update AGENTS to reflect: + +- `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`. + +### Docusaurus + +Update current onboarding pages: + +- development commands; +- testing; +- code generation; +- frontend overview; +- frontend routes; +- frontend API client; +- 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. + +## Implementation Phases + +### Phase 1: Stabilize Default Checks + +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`. + +### Phase 2: Refresh 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. + +### Phase 3: Refresh Documentation + +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`. + +### Phase 4: Full Verification + +Run: + +```bash +npm run lint +npm run test:backend +npm run test:frontend +npm run build:backend +npm run build:frontend +npm run build:docs +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. + +## 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. + +## Spec 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.