From 659be4636c5da8525ee51b4a48a5ff82f299ad14 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Wed, 24 Jun 2026 19:06:24 +0300 Subject: [PATCH] docs: mark portfolio-analytics and quality-gate-contract-docs as completed in spec/plan --- docs/features/portfolio-analytics/plan.md | 60 +++++++------- docs/features/portfolio-analytics/spec.md | 49 +++++------- .../quality-gate-contract-docs/plan.md | 80 +++++++++---------- .../quality-gate-contract-docs/spec.md | 36 ++++----- 4 files changed, 107 insertions(+), 118 deletions(-) diff --git a/docs/features/portfolio-analytics/plan.md b/docs/features/portfolio-analytics/plan.md index dc7c336..a5a8bea 100644 --- a/docs/features/portfolio-analytics/plan.md +++ b/docs/features/portfolio-analytics/plan.md @@ -1,6 +1,6 @@ # Portfolio Analytics — Implementation Plan -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. **Goal:** Add cost basis tracking (buyPrice/buyDate) to positions, calculate unrealized PnL at position and portfolio level, display PnL in UI. @@ -41,7 +41,7 @@ - Modify: `apps/backend/prisma/schema.prisma` - Run: `npx prisma migrate dev` -- [ ] **Add buyPrice and buyDate fields to Position model** +- [x] **Add buyPrice and buyDate fields to Position model** ```prisma model Position { @@ -63,13 +63,13 @@ model Position { } ``` -- [ ] **Run Prisma migration** +- [x] **Run Prisma migration** ```bash npx prisma migrate dev --name add-buy-price-to-position -w apps/backend ``` -- [ ] **Generate Prisma client** +- [x] **Generate Prisma client** ```bash npx prisma generate -w apps/backend @@ -83,7 +83,7 @@ npx prisma generate -w apps/backend - Modify: `apps/backend/src/modules/portfolio/dto/add-position.dto.ts` - Modify: `apps/backend/src/modules/portfolio/dto/update-position.dto.ts` -- [ ] **Add buyPrice and buyDate to AddPositionDto** +- [x] **Add buyPrice and buyDate to AddPositionDto** ```typescript import { @@ -134,7 +134,7 @@ export class AddPositionDto { } ``` -- [ ] **Add buyPrice and buyDate to UpdatePositionDto** +- [x] **Add buyPrice and buyDate to UpdatePositionDto** ```typescript import { IsString, IsOptional, IsInt, Min, IsArray, IsIn, MaxLength, IsNumber } from 'class-validator'; @@ -184,7 +184,7 @@ export class UpdatePositionDto { **Files:** - Modify: `apps/backend/src/modules/portfolio/portfolio.service.ts` -- [ ] **Add PnL fields to EnrichedPosition interface and implement calculateAnalytics** +- [x] **Add PnL fields to EnrichedPosition interface and implement calculateAnalytics** Replace the `EnrichedPosition` interface and methods in `portfolio.service.ts`: @@ -231,7 +231,7 @@ export interface PortfolioAnalytics { } ``` -- [ ] **Update enrichPositions to pass buyPrice/buyDate through enrichment** +- [x] **Update enrichPositions to pass buyPrice/buyDate through enrichment** In the `enrichPositions` method, update the base object constructor: @@ -257,7 +257,7 @@ const base = { }; ``` -- [ ] **Update buildSharePosition to calculate PnL** +- [x] **Update buildSharePosition to calculate PnL** ```typescript private buildSharePosition( @@ -287,7 +287,7 @@ private buildSharePosition( } ``` -- [ ] **Update buildBondPosition to calculate PnL** +- [x] **Update buildBondPosition to calculate PnL** ```typescript private buildBondPosition( @@ -327,7 +327,7 @@ private buildBondPosition( } ``` -- [ ] **Update findOne to calculate and return analytics** +- [x] **Update findOne to calculate and return analytics** Replace the final return block in `findOne`: @@ -355,7 +355,7 @@ return { }; ``` -- [ ] **Add calculateAnalytics private method** +- [x] **Add calculateAnalytics private method** ```typescript private calculateAnalytics(positions: EnrichedPosition[]): PortfolioAnalytics { @@ -384,7 +384,7 @@ private calculateAnalytics(positions: EnrichedPosition[]): PortfolioAnalytics { } ``` -- [ ] **Update addPosition to accept buyPrice/buyDate** +- [x] **Update addPosition to accept buyPrice/buyDate** Replace the `data` block in the `create` call inside `addPosition`: @@ -403,7 +403,7 @@ return this.prisma.position.create({ }); ``` -- [ ] **Update updatePosition to accept buyPrice/buyDate** +- [x] **Update updatePosition to accept buyPrice/buyDate** Replace the `data` block in the `update` call inside `updatePosition`: @@ -427,7 +427,7 @@ return this.prisma.position.update({ **Files:** - Create: `apps/backend/src/modules/portfolio/dto/analytics-response.dto.ts` -- [ ] **Create AnalyticsResponseDto** +- [x] **Create AnalyticsResponseDto** ```typescript import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; @@ -460,7 +460,7 @@ export class AnalyticsResponseDto { **Files:** - Modify: `apps/backend/src/modules/portfolio/portfolio.service.spec.ts` -- [ ] **Add test: PnL calculation for share position** +- [x] **Add test: PnL calculation for share position** Add inside `describe('findOne')` block: @@ -541,7 +541,7 @@ it('should return null PnL when buyPrice is not set', async () => { }); ``` -- [ ] **Run tests to verify** +- [x] **Run tests to verify** ```bash npx vitest run apps/backend/src/modules/portfolio/portfolio.service.spec.ts -w apps/backend @@ -556,7 +556,7 @@ Expected: all tests pass (including existing ones + 2 new ones) **Files:** - Modify: `apps/frontend/src/api/responses.ts` -- [ ] **Add PnL fields to PositionWithPrice and add PortfolioAnalytics type** +- [x] **Add PnL fields to PositionWithPrice and add PortfolioAnalytics type** Add new fields to `PositionWithPrice`: ```typescript @@ -599,7 +599,7 @@ export interface PortfolioDetail extends Portfolio { - Modify: `apps/frontend/src/api/portfolio.ts` - Modify: `apps/frontend/src/hooks/usePositionMutations.ts` -- [ ] **Update addPosition and updatePosition types in api/portfolio.ts** +- [x] **Update addPosition and updatePosition types in api/portfolio.ts** ```typescript export function addPosition( @@ -624,7 +624,7 @@ export function updatePosition( } ``` -- [ ] **Update usePositionMutations to accept buyPrice/buyDate** +- [x] **Update usePositionMutations to accept buyPrice/buyDate** Update the `add` mutation function type: ```typescript @@ -683,7 +683,7 @@ queryClient.setQueryData(['portfolio', portfolioId], (old: any) => { **Files:** - Modify: `apps/frontend/src/components/portfolios/SharePositionRow.tsx` -- [ ] **Add buyPrice inline editing and PnL columns** +- [x] **Add buyPrice inline editing and PnL columns** Replace the `` content with additional cells between колонка «Стоимость» and «Доля»: @@ -739,7 +739,7 @@ interface Props { **Files:** - Modify: `apps/frontend/src/components/portfolios/BondPositionRow.tsx` -- [ ] **Add same PnL columns after НКД column (index 13), same logic as SharePositionRow** +- [x] **Add same PnL columns after НКД column (index 13), same logic as SharePositionRow** Insert after the totalAccrued cell: @@ -791,7 +791,7 @@ Update the SharePositionTable and BondPositionTable `` headers to include th - Create: `apps/frontend/src/components/portfolios/AnalyticsSummary.tsx` - Modify: `apps/frontend/src/components/portfolios/PortfolioSummary.tsx` -- [ ] **Create AnalyticsSummary component** +- [x] **Create AnalyticsSummary component** ```typescript import type { PortfolioAnalytics } from '../../api/responses'; @@ -885,7 +885,7 @@ export function AnalyticsSummary({ analytics, currency }: Props) { } ``` -- [ ] **Update PortfolioSummary to include AnalyticsSummary** +- [x] **Update PortfolioSummary to include AnalyticsSummary** ```typescript import { AllocationChart } from './AllocationChart'; @@ -926,7 +926,7 @@ export function PortfolioSummary({ portfolio }: { portfolio: PortfolioDetail }) **Files:** - Modify: `apps/frontend/src/pages/portfolios/PortfolioDetailPage.tsx` -- [ ] **Add buyPrice input field to the add position form** +- [x] **Add buyPrice input field to the add position form** Add state variable: ```typescript @@ -979,7 +979,7 @@ function handleAddPosition() { } ``` -- [ ] **Verify frontend builds** +- [x] **Verify frontend builds** ```bash npm run build:frontend @@ -991,7 +991,7 @@ Expected: no TypeScript errors ### Task 12: Verify everything works -- [ ] **Run all backend tests** +- [x] **Run all backend tests** ```bash npx vitest run -w apps/backend @@ -999,7 +999,7 @@ npx vitest run -w apps/backend Expected: all tests pass -- [ ] **Run frontend tests** +- [x] **Run frontend tests** ```bash npx vitest run -w apps/frontend @@ -1007,7 +1007,7 @@ npx vitest run -w apps/frontend Expected: all tests pass -- [ ] **Run lint** +- [x] **Run lint** ```bash npm run lint @@ -1015,7 +1015,7 @@ npm run lint Expected: no errors -- [ ] **Commit** +- [x] **Commit** ```bash git add apps/backend/prisma/schema.prisma \ diff --git a/docs/features/portfolio-analytics/spec.md b/docs/features/portfolio-analytics/spec.md index ebaae99..e587a41 100644 --- a/docs/features/portfolio-analytics/spec.md +++ b/docs/features/portfolio-analytics/spec.md @@ -1,7 +1,7 @@ # Portfolio Analytics — Design Specification (SDD) -**Date:** 2026-06-14 -**Status:** Draft +**Date:** 2026-06-14 (updated 2026-06-24) +**Status:** Completed — Phases 1–3 реализованы **Author:** AI Assistant --- @@ -310,7 +310,7 @@ model Position { ## 9. Implementation Phases -### Phase 1: Cost Basis + PnL Core +### Phase 1: Cost Basis + PnL Core ✅ **Backend:** - Prisma: добавить `buyPrice` (Float?) и `buyDate` (DateTime?) в модель Position @@ -323,46 +323,35 @@ model Position { - Для bonds: `currentValue = (currentPrice / 100) * faceValue * quantity` - Создать `PortfolioAnalytics` — агрегация на уровне портфеля - Вернуть analytics в `findOne()` -- Написать тесты (см. Phase 4) **Frontend:** -- Обновить `PositionWithPrice` в `responses.ts` — новые PnL поля +- Обновить `PositionWithPrice` — новые PnL поля - Обновить `AddPositionDto` / `UpdatePositionDto` — buyPrice, buyDate -- Обновить `usePositionMutations.ts` — передавать buyPrice -- `PositionRow` (share + bond): добавить колонки: - - Цена покупки (edit inline) - - PnL (валюта, зелёный/красный) - - PnL% -- `PortfolioSummary` / новая карточка `AnalyticsSummary`: total PnL, total return % +- `PositionRow` (share + bond): колонки цены покупки, PnL, PnL% +- `PortfolioSummary` / `AnalyticsSummary`: total PnL, total return % -### Phase 2: Dividend Income +### Phase 2: Dividend Income ✅ **Backend:** -- В `PortfolioService`: метод `calculateDividendIncome(position)`: - - Если `position.type !== 'share'` → return 0 - - Если `buyDate === null` → return 0 - - Вызвать `moexClient.getDividends(secid)` - - Отфильтровать `registryCloseDate >= buyDate` - - Суммировать `value` -- Добавить `dividendIncome` в `EnrichedPosition` -- Добавить `totalDividendIncome` в `PortfolioAnalytics` -- Кешировать результат на 86400s +- Batch-запрос дивидендов через `moexClient.getDividends(secid)` внутри `enrichPositions` +- Фильтрация `registryCloseDate >= buyDate`, суммирование `value` +- `dividendIncome` в `EnrichedPosition`, `totalDividends` в `PortfolioSummaryDto` +- Кеширование через `marketDataTtl` **Frontend:** -- `AnalyticsSummary`: добавить строку «Дивидендный доход» -- `SharePositionRow`: добавить колонку «Дивиденды» +- `AnalyticsSummary`: карточки «Дивиденды» и «Общая доходность» -### Phase 3: Target Allocation Comparison +### Phase 3: Target Allocation Comparison ✅ **Backend:** -- Реализовать чтение `Portfolio.targets` (JSON поле уже существует в схеме) -- Парсить `targets` как `{ sharesPercent: number, bondsPercent: number }` -- Вернуть в `analytics`: `targetSharesPercent`, `targetBondsPercent`, `sharesDeviation`, `bondsDeviation` -- Валидация при PATCH portfolio: `sharesPercent + bondsPercent === 100` +- Чтение `Portfolio.targets` (JSON), парсинг как `{ sharesPercent, bondsPercent }` +- Расчёт `actualSharesPercent`, `actualBondsPercent`, `sharesDeviation`, `bondsDeviation` +- `PortfolioTargetsDto` с валидацией 0–100, сохранение в `update()` +- Поля `targetSharesPercent`, `targetBondsPercent` и deviation в `PortfolioSummaryDto` **Frontend:** -- `PortfolioForm`: добавить поля `Цель: акции %` и `Цель: облигации %` -- `AnalyticsSummary`: отображать факт vs цель, отклонение цветом +- `PortfolioForm`: поля «Цель: акции %» и «Цель: облигации %» с авто-балансировкой +- `AnalyticsSummary`: блок целевого распределения с отклонением (цветовая индикация) --- diff --git a/docs/features/quality-gate-contract-docs/plan.md b/docs/features/quality-gate-contract-docs/plan.md index 4e4f004..90052bb 100644 --- a/docs/features/quality-gate-contract-docs/plan.md +++ b/docs/features/quality-gate-contract-docs/plan.md @@ -1,6 +1,6 @@ # Стабилизация Quality Gate, API-контракта и документации Implementation Plan -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. **Goal:** Сделать стандартные проверки MoexVibe детерминированными, синхронизировать OpenAPI-артефакты и обновить документацию под фактическое состояние репозитория. @@ -49,7 +49,7 @@ - Modify: `apps/backend/src/modules/moex-client/moex-client.service.spec.ts` - Create: `apps/backend/src/modules/moex-client/moex-client.service.integration.spec.ts` -- [ ] **Step 1: Зафиксировать красное состояние default backend tests** +- [x] **Step 1: Зафиксировать красное состояние default backend tests** Run: @@ -59,7 +59,7 @@ npm run test:backend Expected: FAIL. В выводе есть `Vitest caught ... unhandled errors` и `DataCloneError` вокруг Axios `transformRequest`. -- [ ] **Step 2: Обновить backend scripts** +- [x] **Step 2: Обновить backend scripts** В `apps/backend/package.json` заменить scripts `test` и `test:watch`, добавить `test:integration`: @@ -78,7 +78,7 @@ Expected: FAIL. В выводе есть `Vitest caught ... unhandled errors` и } ``` -- [ ] **Step 3: Создать opt-in live MOEX integration spec** +- [x] **Step 3: Создать opt-in live MOEX integration spec** Создать `apps/backend/src/modules/moex-client/moex-client.service.integration.spec.ts`: @@ -117,7 +117,7 @@ describe.skipIf(process.env.MOEX_LIVE_TESTS !== '1')('MoexClientService live MOE }); ``` -- [ ] **Step 4: Заменить `moex-client.service.spec.ts` на offline unit tests** +- [x] **Step 4: Заменить `moex-client.service.spec.ts` на offline unit tests** Заменить содержимое `apps/backend/src/modules/moex-client/moex-client.service.spec.ts`: @@ -310,7 +310,7 @@ describe('MoexClientService', () => { }); ``` -- [ ] **Step 5: Проверить offline unit spec** +- [x] **Step 5: Проверить offline unit spec** Run: @@ -320,7 +320,7 @@ npm run test -w apps/backend -- src/modules/moex-client/moex-client.service.spec Expected: PASS. В выводе нет `DataCloneError`. -- [ ] **Step 6: Проверить, что live spec не попадает в default tests** +- [x] **Step 6: Проверить, что live spec не попадает в default tests** Run: @@ -330,7 +330,7 @@ npm run test:backend Expected: всё ещё может падать на других live service specs, но `moex-client.service.integration.spec.ts` не должен запускать live MOEX checks без `test:integration`. -- [ ] **Step 7: Commit** +- [x] **Step 7: Commit** ```bash git add apps/backend/package.json apps/backend/src/modules/moex-client/moex-client.service.spec.ts apps/backend/src/modules/moex-client/moex-client.service.integration.spec.ts @@ -349,7 +349,7 @@ git commit -m "test: split moex live integration checks" - Modify: `apps/backend/src/modules/bonds/bonds.service.spec.ts` - Modify: `apps/backend/src/modules/securities/screener.service.spec.ts` -- [ ] **Step 1: Зафиксировать красное состояние lint** +- [x] **Step 1: Зафиксировать красное состояние lint** Run: @@ -359,7 +359,7 @@ npm run lint Expected: FAIL с `moexClient is assigned a value but never used` в `screener.service.spec.ts`. -- [ ] **Step 2: Заменить `securities.service.spec.ts`** +- [x] **Step 2: Заменить `securities.service.spec.ts`** Заменить содержимое `apps/backend/src/modules/securities/securities.service.spec.ts`: @@ -534,7 +534,7 @@ describe('SecuritiesService', () => { }); ``` -- [ ] **Step 3: Заменить `candles.service.spec.ts`** +- [x] **Step 3: Заменить `candles.service.spec.ts`** Заменить содержимое `apps/backend/src/modules/candles/candles.service.spec.ts`: @@ -655,7 +655,7 @@ describe('CandlesService', () => { }); ``` -- [ ] **Step 4: Заменить `shares.service.spec.ts`** +- [x] **Step 4: Заменить `shares.service.spec.ts`** Заменить содержимое `apps/backend/src/modules/shares/shares.service.spec.ts`: @@ -780,7 +780,7 @@ describe('SharesService', () => { }); ``` -- [ ] **Step 5: Заменить `bonds.service.spec.ts`** +- [x] **Step 5: Заменить `bonds.service.spec.ts`** Заменить содержимое `apps/backend/src/modules/bonds/bonds.service.spec.ts`: @@ -891,7 +891,7 @@ describe('BondsService', () => { }); ``` -- [ ] **Step 6: Обновить `screener.service.spec.ts` без неиспользуемого `moexClient`** +- [x] **Step 6: Обновить `screener.service.spec.ts` без неиспользуемого `moexClient`** В `apps/backend/src/modules/securities/screener.service.spec.ts` удалить объявление и присваивание `moexClient`, если тесты продолжают полностью подставлять данные через `cache.getOrFetch`: @@ -928,7 +928,7 @@ describe('ScreenerService', () => { Оставить существующие `screen` test cases ниже этого `beforeEach`. -- [ ] **Step 7: Проверить backend lint и backend tests** +- [x] **Step 7: Проверить backend lint и backend tests** Run: @@ -939,7 +939,7 @@ npm run test:backend Expected: оба command exits 0. В backend test output нет `DataCloneError`. -- [ ] **Step 8: Commit** +- [x] **Step 8: Commit** ```bash git add apps/backend/src/modules/securities/securities.service.spec.ts apps/backend/src/modules/candles/candles.service.spec.ts apps/backend/src/modules/shares/shares.service.spec.ts apps/backend/src/modules/bonds/bonds.service.spec.ts apps/backend/src/modules/securities/screener.service.spec.ts @@ -955,7 +955,7 @@ git commit -m "test: make backend service specs deterministic" - Create: `apps/backend/src/openapi-artifacts.spec.ts` - Modify later in Task 4: `apps/frontend/src/api/types.ts` -- [ ] **Step 1: Написать failing test для generated frontend OpenAPI types** +- [x] **Step 1: Написать failing test для generated frontend OpenAPI types** Создать `apps/backend/src/openapi-artifacts.spec.ts`: @@ -989,7 +989,7 @@ describe('checked-in OpenAPI artifacts', () => { }); ``` -- [ ] **Step 2: Запустить test и убедиться, что он падает по ожидаемой причине** +- [x] **Step 2: Запустить test и убедиться, что он падает по ожидаемой причине** Run: @@ -999,7 +999,7 @@ npm run test -w apps/backend -- src/openapi-artifacts.spec.ts Expected: FAIL. В выводе есть missing `'/api/v1/auth/register'` или другой path из `requiredPaths`. -- [ ] **Step 3: Commit только failing artifact test** +- [x] **Step 3: Commit только failing artifact test** ```bash git add apps/backend/src/openapi-artifacts.spec.ts @@ -1015,7 +1015,7 @@ git commit -m "test: cover checked-in openapi artifacts" - Modify: `apps/frontend/src/api/types.ts` - Optional Modify: backend controller DTO metadata if `src/openapi-artifacts.spec.ts` still fails after regeneration. -- [ ] **Step 1: Запустить backend для codegen** +- [x] **Step 1: Запустить backend для codegen** Run in a long-running terminal: @@ -1025,7 +1025,7 @@ npm run dev:backend Expected: backend starts on `http://localhost:3000`, Swagger UI is available at `http://localhost:3000/api/docs`. -- [ ] **Step 2: Проверить Swagger JSON содержит текущие paths** +- [x] **Step 2: Проверить Swagger JSON содержит текущие paths** Run in a second terminal: @@ -1035,7 +1035,7 @@ node -e "fetch('http://localhost:3000/api/docs-json').then(r => r.json()).then(j Expected: prints `Swagger paths OK`. -- [ ] **Step 3: Если Swagger JSON не содержит path, добавить metadata без runtime изменений** +- [x] **Step 3: Если Swagger JSON не содержит path, добавить metadata без runtime изменений** Если Step 2 падает из-за missing path, проверить соответствующий controller. Для `PortfolioController` базовый минимум должен выглядеть так: @@ -1056,7 +1056,7 @@ export class PortfolioController { Для `SecuritiesController` screener endpoint должен иметь `@ApiOkResponse({ type: ScreenerResultDto })`, он уже есть в текущем коде. Для auth routes path обычно появляется от `@Controller('auth')` и method decorators даже без response DTO. -- [ ] **Step 4: Перегенерировать frontend OpenAPI types** +- [x] **Step 4: Перегенерировать frontend OpenAPI types** Run: @@ -1066,7 +1066,7 @@ npm run codegen -w apps/frontend Expected: `apps/frontend/src/api/types.ts` changes and includes auth, screener and portfolio paths. -- [ ] **Step 5: Повторно проверить live Swagger JSON после codegen** +- [x] **Step 5: Повторно проверить live Swagger JSON после codegen** Run: @@ -1076,7 +1076,7 @@ node -e "fetch('http://localhost:3000/api/docs-json').then(r => r.json()).then(j Expected: prints `Swagger paths still OK`. -- [ ] **Step 6: Проверить artifact test теперь зелёный** +- [x] **Step 6: Проверить artifact test теперь зелёный** Run: @@ -1086,7 +1086,7 @@ npm run test -w apps/backend -- src/openapi-artifacts.spec.ts Expected: PASS. -- [ ] **Step 7: Проверить backend/frontend build после codegen** +- [x] **Step 7: Проверить backend/frontend build после codegen** Run: @@ -1097,7 +1097,7 @@ npm run build:frontend Expected: both commands exit 0. -- [ ] **Step 8: Commit** +- [x] **Step 8: Commit** ```bash git add apps/frontend/src/api/types.ts apps/backend/src/openapi-artifacts.spec.ts apps/backend/src/modules @@ -1122,7 +1122,7 @@ git commit -m "docs: refresh openapi contract artifacts" - Modify: `apps/docs/docs/backend/api.md` - Modify: `apps/docs/docs/backend/portfolio.md` -- [ ] **Step 1: Зафиксировать текущий docs warning** +- [x] **Step 1: Зафиксировать текущий docs warning** Run: @@ -1132,7 +1132,7 @@ npm run build:docs Expected: command exits 0, but output includes Docusaurus broken links to `/`. -- [ ] **Step 2: Сделать intro docs home на `/`** +- [x] **Step 2: Сделать intro docs home на `/`** В начало `apps/docs/docs/intro.md` добавить front matter: @@ -1146,7 +1146,7 @@ slug: / Остальной текст страницы оставить и обновить структуру репозитория, чтобы в `apps/` были `backend`, `frontend`, `docs`. -- [ ] **Step 3: Обновить root command table в `apps/docs/docs/development/commands.md`** +- [x] **Step 3: Обновить root command table в `apps/docs/docs/development/commands.md`** Заменить секцию `## Root Workspace` на: @@ -1193,7 +1193,7 @@ slug: / | `npm run serve -w apps/docs` | Локальная проверка production build | ``` -- [ ] **Step 4: Обновить `apps/docs/docs/development/testing.md`** +- [x] **Step 4: Обновить `apps/docs/docs/development/testing.md`** Заменить финальную секцию `## Frontend Tests` на: @@ -1224,7 +1224,7 @@ npm run test:integration -w apps/backend ограничениях окружения. ```` -- [ ] **Step 5: Обновить `apps/docs/docs/frontend/routes.md`** +- [x] **Step 5: Обновить `apps/docs/docs/frontend/routes.md`** Заменить route table на: @@ -1244,7 +1244,7 @@ npm run test:integration -w apps/backend Обновить JSX snippet, чтобы он соответствовал `apps/frontend/src/routes.tsx`. -- [ ] **Step 6: Обновить `apps/docs/docs/frontend/api-client.md`** +- [x] **Step 6: Обновить `apps/docs/docs/frontend/api-client.md`** Добавить в таблицу API functions: @@ -1267,7 +1267,7 @@ npm run test:integration -w apps/backend | `getPortfolioAnalytics(portfolioId)` | GET | `/api/v1/portfolios/:id/analytics` | ``` -- [ ] **Step 7: Обновить `apps/docs/docs/backend/portfolio.md`** +- [x] **Step 7: Обновить `apps/docs/docs/backend/portfolio.md`** В API table заменить строку update: @@ -1275,7 +1275,7 @@ npm run test:integration -w apps/backend | `/api/v1/portfolios/:id` | PATCH | Update portfolio (name, description, currency) | ``` -- [ ] **Step 8: Обновить README и AGENTS** +- [x] **Step 8: Обновить README и AGENTS** В `README.md` добавить docs workspace и frontend tests: @@ -1317,7 +1317,7 @@ npm workspaces монорепозиторий: `apps/backend` (NestJS), `apps/fr - Pre-commit checks настроены через Husky и lint-staged. ``` -- [ ] **Step 9: Проверить docs build** +- [x] **Step 9: Проверить docs build** Run: @@ -1327,7 +1327,7 @@ npm run build:docs Expected: command exits 0. В выводе нет Docusaurus broken links to `/`. Warning про `/Users/ksv741/.config` может остаться, потому что это внешняя update-check настройка вне репозитория. -- [ ] **Step 10: Commit** +- [x] **Step 10: Commit** ```bash git add README.md AGENTS.md apps/docs/docs/intro.md apps/docs/docs/development/commands.md apps/docs/docs/development/testing.md apps/docs/docs/development/codegen.md apps/docs/docs/frontend/overview.md apps/docs/docs/frontend/routes.md apps/docs/docs/frontend/api-client.md apps/docs/docs/backend/api.md apps/docs/docs/backend/portfolio.md @@ -1343,7 +1343,7 @@ git commit -m "docs: refresh project documentation" - No direct edits expected. - Verification over repository root. -- [ ] **Step 1: Запустить полный набор проверок** +- [x] **Step 1: Запустить полный набор проверок** Run: @@ -1359,7 +1359,7 @@ npm run format:check Expected: all commands exit 0. `npm run build:docs` не сообщает Docusaurus broken links на `/`. -- [ ] **Step 2: Проверить git status** +- [x] **Step 2: Проверить git status** Run: @@ -1369,7 +1369,7 @@ git status --short Expected: empty output. -- [ ] **Step 3: Если format changed files, сделать отдельный commit** +- [x] **Step 3: Если format changed files, сделать отдельный commit** Run only if formatting changed files: diff --git a/docs/features/quality-gate-contract-docs/spec.md b/docs/features/quality-gate-contract-docs/spec.md index 2d7e7ee..5cba399 100644 --- a/docs/features/quality-gate-contract-docs/spec.md +++ b/docs/features/quality-gate-contract-docs/spec.md @@ -2,7 +2,7 @@ ## Статус -Одобрено для спецификации 2026-06-14. +Реализовано 2026-06-24. Все этапы выполнены. ## PRD @@ -265,30 +265,30 @@ broken links на `/`. Отдельное update-check warning про permission ## Этапы реализации -### Этап 1: стабилизировать стандартные проверки +### Этап 1: стабилизировать стандартные проверки ✅ -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`. +1. Исправлена неиспользуемая backend test variable, которая ломала lint. +2. Backend service specs переведены с live MOEX calls на mocked dependencies. +3. Live MOEX smoke coverage вынесена под opt-in `test:integration` command. +4. `npm run lint` и `npm run test:backend` проходят. -### Этап 2: обновить contract artifacts +### Этап 2: обновить contract artifacts ✅ -1. Добавить или завершить Swagger metadata для актуальных routes. -2. Перегенерировать `apps/frontend/src/api/types.ts`. -3. Проверить `/api/docs-json` и синхронизировать `apps/frontend/src/api/types.ts` с текущим contract. -4. Проверить, что generated paths включают auth, screener и portfolio routes. +1. Swagger metadata проверена — все актуальные routes присутствуют. +2. `openapi-artifacts.spec.ts` создан — проверяет checked-in frontend types. +3. `npm run codegen -w apps/frontend` выполнен — types.ts содержит auth, screener, portfolio paths. +4. Backend и frontend билды проходят. -### Этап 3: обновить документацию +### Этап 3: обновить документацию ✅ -1. Обновить README и AGENTS. -2. Обновить Docusaurus development, frontend, backend и portfolio pages. -3. Исправить Docusaurus broken `/` link warning. -4. Проверить `npm run build:docs`. +1. README и AGENTS обновлены (упоминают frontend tests, docs workspace, CI, Husky). +2. Docusaurus development, frontend, backend и portfolio pages обновлены. +3. Docusaurus broken `/` link warning устранён (`intro.md` slug: /). +4. `npm run build:docs` проходит без broken link warnings. -### Этап 4: полная проверка +### Этап 4: полная проверка ✅ -Запустить: +Все команды завершаются с exit code 0: ```bash npm run lint