codex/tech-debt-sdd-tdd #12
@ -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.
|
||||||
Loading…
x
Reference in New Issue
Block a user