From 972356ee69085f5447f7bac00c9882c27d1a0edc Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Fri, 14 Aug 2026 09:49:55 +0300 Subject: [PATCH] add: refactor foundation --- .../.openspec.yaml | 2 + .../design.md | 80 +++++++++++++++++++ .../proposal.md | 28 +++++++ .../specs/api-contract-generation/spec.md | 40 ++++++++++ .../tasks.md | 26 ++++++ .../specs/api-contract-generation/spec.md | 29 ++++++- 6 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-foundation/.openspec.yaml create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-foundation/design.md create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-foundation/proposal.md create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-foundation/specs/api-contract-generation/spec.md create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-foundation/tasks.md diff --git a/openspec/changes/archive/2026-08-14-align-frontend-foundation/.openspec.yaml b/openspec/changes/archive/2026-08-14-align-frontend-foundation/.openspec.yaml new file mode 100644 index 0000000..4af8641 --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-foundation/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-08-14 diff --git a/openspec/changes/archive/2026-08-14-align-frontend-foundation/design.md b/openspec/changes/archive/2026-08-14-align-frontend-foundation/design.md new file mode 100644 index 0000000..8934418 --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-foundation/design.md @@ -0,0 +1,80 @@ +## Контекст + +Frontend уже использует React, FSD, generated OpenAPI types и React Query, но получает OpenAPI непосредственно из работающего backend и вручную создаёт query/mutation hooks. Bearer header одновременно добавляется общим HTTP transport и передаётся отдельными вызовами. Локальная проверка состоит из разрозненных команд и не проверяет generated artifact, не изолирует API-тесты и не контролирует границы FSD-слоёв. + +Причина изменения описана в `proposal.md`, а контрактные требования — в `specs/api-contract-generation/spec.md`. + +## Цели и нецели + +**Цели:** + +- Сделать настройку frontend, генерацию API-контракта и локальную проверку воспроизводимыми без запущенного backend, кроме явной синхронизации контракта. +- Централизовать типизированную интеграцию React Query и auth transport без изменения существующих пользовательских сценариев и семантики cache. +- Создать чистый, проверяемый и ориентированный на агентов baseline разработки с API mocks только для тестов. +- Зафиксировать реализованный workflow в документации frontend и постоянной документации проекта. + +**Нецели:** + +- Не мигрируются routes, forms, state-views, view-model и ErrorBoundary; они относятся к `align-frontend-ui-patterns`. +- Не добавляются Storybook, Playwright, browser MSW worker, mock-режим разработки и CI configuration. +- Не меняются владение backend OpenAPI, API routes, authentication protocol и доменная терминология WealthLedger. + +## Решения + +### Воспроизводимый frontend baseline + +Frontend будет объявлять Node 22 через `.nvmrc` и `package.json#engines`. Зависимости будут использовать точные версии, а lockfile будет перегенерирован. Toolchain перейдёт на совместимый baseline template, включая Mantine 9, строгие опции TypeScript, aliases Vite/TypeScript и tooling статического анализа. + +Aliases будут одинаково определены в Vite и TypeScript. Существующие cross-slice imports будут использовать публичные API через `index.ts`; файлы внутри slice могут использовать относительные imports. `dependency-cruiser` будет проверять только направление FSD-слоёв, а использование public API останется документированным правилом review. + +Точные версии выбраны вместо semver ranges, чтобы установки агентов и разработчиков воспроизводили один dependency graph. Собственный Node version checker отклонён: `.nvmrc` и `engines` сообщают ограничение без дополнительного пути к ошибке. + +### Генерация OpenAPI из snapshot + +`src/shared/api/spec/` будет хранить committed raw JSON snapshot из backend OpenAPI endpoint, а `src/shared/api/generated/schema.d.ts` останется committed generated artifact. Явная sync-команда получит endpoint, настроенный `VITE_API_BASE_URL`, провалидирует его генерацией и заменит оба artifacts только после успеха всех шагов. Временные файлы не позволят неудачному fetch или generation частично обновить контракт. + +`openapi:generate` будет генерировать из committed snapshot. `openapi:check` независимо сгенерирует ожидаемую schema во временную директорию и сравнит её с committed artifact. Команда не обращается к backend, поэтому `verify` остаётся доступным offline. + +Генерация напрямую из работающего backend отклонена, потому что обычные локальные проверки станут зависимы от его доступности. Преобразование snapshot в YAML отклонено, потому что сохранение опубликованного JSON исключает ненужную трансформацию. + +### Типизированная интеграция queries и auth boundary + +Shared OpenAPI client остаётся единственной HTTP entry point. `openapi-react-query` будет получать из него типизированные React Query operations; существующие query keys, `enabled`, stale times, retry policies, invalidations и отображение ошибок сохранятся. API request и response types продолжат выводиться из generated `paths`, а не дублировать DTOs. + +Реализация authenticated fetch становится единственным владельцем Bearer header. Она исключает auth endpoints, делает ровно один retry только после `401` с `UNAUTHORIZED` и успешного refresh, а при неудачном refresh очищает session state. Query и mutation calls больше не передают Authorization самостоятельно. Initial session restoration должен обновлять и React state, и token reference, используемый transport; logout и неудачный refresh очищают оба и cache QueryClient. + +Ручная передача headers отклонена: она дублирует cross-cutting security concern и может разойтись с поведением transport. Замена refresh semantics на library interceptor отклонена, потому что существующие refresh single-flight и browser lock уже являются корректным product behavior. + +### MSW только для тестов и целевые regression tests + +MSW будет запускаться только через Vitest setup и handlers для текущего WealthLedger API. Не будут добавлены service worker, команда `dev:mock`, mock build и application mock mode. Поэтому unit и hook tests смогут выполнять успешные и ошибочные API scenarios без backend. + +Целевые transport tests проверят Bearer injection, исключение auth endpoints, один refresh/retry для `UNAUTHORIZED`, очистку session после неудачного refresh и авторизацию восстановленной session. Coverage формируется как report, но не получает числовой threshold до стабилизации UI patterns. + +Browser mocking отклонён, потому что он создаёт незапрошенный второй application mode. Coverage threshold отклонён, потому что текущий baseline слишком мал для стабильной и содержательной цели. + +### Единый локальный quality gate и постоянная документация + +`npm run verify` будет запускать OpenAPI drift detection, typecheck, Biome, unit tests, coverage reporting, Knip, dependency-cruiser и production build. Все проверки должны проходить без baseline suppressions или allowlists. Husky устанавливается через `prepare`; pre-commit форматирует staged files и запускает contract и dependency checks, а commit-msg проверяет Conventional Commits. + +Frontend `AGENTS.md` остаётся коротким индексом «задача → документ». Его локальные docs описывают API workflow, test setup, structure и conventions. После реализации `docs/development/` получит постоянное cross-repository описание frontend workflow: требуемую Node version, contract synchronization и quality-gate commands. Так знания о реализованном процессе не останутся только в frontend repository. + +Запуск полного gate в каждом pre-commit hook отклонён, потому что tests и builds замедляют обычные commits; `verify` остаётся единственным явным полным gate. Storybook и e2e build steps исключены вместе с их tools. + +## Риски и компромиссы + +- [Обновления Mantine и совместимых зависимостей могут выявить несовместимости исходного кода] → Устранить все type, lint и build errors до принятия нового lockfile. +- [Committed snapshot может отставать от backend] → Backend остаётся источником истины; явный sync обновляет snapshot, а локальная проверка гарантирует только внутреннюю согласованность. +- [Централизация Bearer injection может сломать восстановленные sessions] → Синхронизировать token reference во время initial refresh и покрыть поведение transport tests. +- [Static analyzers могут сообщить о существующем коде] → Исправить код или точно настроить tool до получения зелёного baseline; не скрывать findings временными waivers. +- [Hooks запускаются не во всех окружениях разработчиков] → `npm run verify` остаётся независимо запускаемым и документированным completion gate. + +## План миграции + +1. Обновить и зафиксировать frontend toolchain, настроить aliases и static-analysis tools, привести текущий исходный код к новым checks. +2. Добавить OpenAPI snapshot, sync/generate/check commands, затем проверить generated artifact из committed snapshot без доступа к backend. +3. Мигрировать API queries и mutations на typed integration, централизовать Bearer handling и добавить transport regression coverage. +4. Добавить MSW только для тестов, coverage reporting, git hooks и единый скрипт `verify`. +5. Обновить frontend agent documentation и `docs/development/` описанием реализованного workflow, затем запустить полный quality gate. + +Изменение локально для frontend и может быть отменено откатом его frontend и documentation commits. Backend contract не меняется. diff --git a/openspec/changes/archive/2026-08-14-align-frontend-foundation/proposal.md b/openspec/changes/archive/2026-08-14-align-frontend-foundation/proposal.md new file mode 100644 index 0000000..7023442 --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-foundation/proposal.md @@ -0,0 +1,28 @@ +## Why + +Текущий WealthLedger frontend имеет базовый React/FSD-стек, но не содержит проверенный агентский workflow из `references/frontend-ai-template`: воспроизводимую среду, единый quality gate, проверку дрейфа OpenAPI и изолированные API-тесты. Это делает работу агентов зависимой от локального backend и ручной дисциплины. + +## What Changes + +- Выравнивается frontend toolchain с проверенным template: Node 22, фиксированные версии зависимостей, strict TypeScript, aliases, Mantine 9 и tooling для статического анализа. +- Frontend хранит версионируемый snapshot опубликованного backend OpenAPI, генерирует schema из него и проверяет отсутствие дрейфа generated artifact. +- Typed API layer переходит на `openapi-react-query`, сохраняя текущую auth-семантику Bearer token, refresh и retry. +- Добавляются MSW handlers только для unit-тестов, единый `verify`, coverage report, git hooks и Conventional Commits. +- Документация и `AGENTS.md` расширяются до agent-oriented структуры template, без переноса Todo-домена. +- Playwright, Storybook и CI не входят в это изменение. + +## Capabilities + +### New Capabilities + + + +### Modified Capabilities + +- `api-contract-generation`: frontend хранит проверяемый snapshot backend OpenAPI и детерминированно генерирует из него transport types. + +## Impact + +- Затрагивает `frontend` package/configuration, API client, тестовую инфраструктуру, локальную документацию и git hooks. +- Backend остаётся источником истины для OpenAPI и используется только командой синхронизации snapshot. +- Требует обновления зависимостей и lockfile frontend-репозитория. diff --git a/openspec/changes/archive/2026-08-14-align-frontend-foundation/specs/api-contract-generation/spec.md b/openspec/changes/archive/2026-08-14-align-frontend-foundation/specs/api-contract-generation/spec.md new file mode 100644 index 0000000..20057b6 --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-foundation/specs/api-contract-generation/spec.md @@ -0,0 +1,40 @@ +## MODIFIED Requirements + +### Requirement: Frontend использует сгенерированный API-клиент +Frontend SHALL хранить в репозитории версионируемый snapshot опубликованного OpenAPI-документа и генерировать transport types из этого snapshot. Команда синхронизации MUST получать документ из backend, а затем обновлять snapshot и сгенерированный результат только при успешном завершении всего процесса. Сгенерированный результат MUST храниться в frontend-репозитории. + +#### Scenario: API-контракт синхронизируется +- **WHEN** разработчик запускает команду синхронизации при доступном backend с валидным OpenAPI-документом +- **THEN** frontend обновляет versioned snapshot и сгенерированные transport types из опубликованного контракта + +#### Scenario: API-контракт изменяется +- **WHEN** backend меняет опубликованный API-контракт +- **THEN** frontend может синхронизировать snapshot и повторно сгенерировать transport types до интеграции изменения + +#### Scenario: Синхронизация контракта завершается ошибкой +- **WHEN** backend недоступен или публикует невалидный OpenAPI-документ +- **THEN** команда завершается с ошибкой и существующие snapshot и сгенерированные transport types не изменяются + +### Requirement: Frontend не зависит от OpenAPI во время работы +Работающий frontend MUST использовать ранее сгенерированные transport types и MUST NOT требовать доступность Swagger или OpenAPI endpoint во время запуска, выполнения пользовательского сценария или локальной проверки актуальности generated artifact. Доступ к backend OpenAPI endpoint разрешён только команде синхронизации snapshot. + +#### Scenario: OpenAPI endpoint недоступен после сборки frontend +- **WHEN** frontend уже собран, а endpoint OpenAPI временно недоступен +- **THEN** frontend продолжает использовать сгенерированный код для запросов к API + +#### Scenario: Локальная проверка выполняется без backend +- **WHEN** разработчик запускает локальный quality gate без доступного backend +- **THEN** проверка актуальности generated artifact использует сохранённый snapshot и не обращается к OpenAPI endpoint + +## ADDED Requirements + +### Requirement: Frontend обнаруживает дрейф сгенерированного контракта +Frontend SHALL проверять, что committed generated transport types соответствуют versioned OpenAPI snapshot, и MUST завершать quality gate с ошибкой при расхождении. + +#### Scenario: Generated artifact устарел +- **WHEN** OpenAPI snapshot изменён, а generated transport types не обновлены +- **THEN** проверка контракта завершается с ошибкой + +#### Scenario: Generated artifact изменён вручную +- **WHEN** generated transport types отличаются от результата генерации из сохранённого snapshot +- **THEN** проверка контракта завершается с ошибкой diff --git a/openspec/changes/archive/2026-08-14-align-frontend-foundation/tasks.md b/openspec/changes/archive/2026-08-14-align-frontend-foundation/tasks.md new file mode 100644 index 0000000..e190256 --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-foundation/tasks.md @@ -0,0 +1,26 @@ +## 1. Воспроизводимый baseline + +- [x] 1.1 Зафиксировать Node 22, exact dependency versions и обновлённый lockfile; обновить совместимые React/Mantine/TypeScript toolchain configuration. +- [x] 1.2 Настроить Vite/TypeScript aliases, Knip и `dependency-cruiser`; перевести существующие межслайсовые импорты на public `index.ts` APIs и устранить все findings новых checks. + +## 2. Локальный OpenAPI contract + +- [x] 2.1 Добавить committed raw OpenAPI JSON snapshot и явную атомарную sync-команду, получающую опубликованный backend contract только по запросу разработчика. +- [x] 2.2 Генерировать `schema.d.ts` из committed snapshot и добавить offline `openapi:check`, который обнаруживает устаревший или вручную изменённый generated artifact. + +## 3. Типизированный API и тесты + +- [x] 3.1 Подключить `openapi-react-query` к shared typed client и мигрировать текущие API queries и mutations, сохранив cache, retry и UI error semantics. +- [x] 3.2 Сделать authenticated fetch единственным владельцем Bearer header; сохранить refresh single-flight/retry semantics и синхронизацию token reference при initial session restoration. +- [x] 3.3 Добавить MSW только в Vitest и handlers текущего API; покрыть transport auth invariants и изолированные успешные и ошибочные API scenarios. + +## 4. Единый quality gate + +- [x] 4.1 Добавить coverage report и `npm run verify` с OpenAPI drift, typecheck, Biome, unit tests, coverage, Knip, FSD dependency checks и production build. +- [x] 4.2 Настроить Husky, lint-staged и Conventional Commits: быстрый pre-commit для staged formatting, contract/dependency checks и commit-msg validation. + +## 5. Документация и проверка + +- [x] 5.1 Обновить frontend `AGENTS.md` и тематические docs для agent-oriented workflow без UI, Storybook и e2e guidance. +- [x] 5.2 Добавить в `docs/development/` постоянную документацию реализованного frontend workflow: Node version, contract sync и quality gate. +- [x] 5.3 Выполнить `npm ci` и `npm run verify`; убедиться, что baseline проходит без suppressions и без доступного backend после завершения sync. diff --git a/openspec/specs/api-contract-generation/spec.md b/openspec/specs/api-contract-generation/spec.md index af43237..04e096c 100644 --- a/openspec/specs/api-contract-generation/spec.md +++ b/openspec/specs/api-contract-generation/spec.md @@ -14,15 +14,38 @@ Backend SHALL публиковать версионированный OpenAPI-д - **THEN** разработчик может получить OpenAPI-документ для генерации frontend-клиента ### Requirement: Frontend использует сгенерированный API-клиент -Frontend SHALL генерировать типы и модуль HTTP-запросов из опубликованного OpenAPI-документа. Сгенерированный результат MUST храниться в frontend-репозитории и обновляться локальной воспроизводимой командой. +Frontend SHALL хранить в репозитории версионируемый snapshot опубликованного OpenAPI-документа и генерировать transport types из этого snapshot. Команда синхронизации MUST получать документ из backend, а затем обновлять snapshot и сгенерированный результат только при успешном завершении всего процесса. Сгенерированный результат MUST храниться в frontend-репозитории. + +#### Scenario: API-контракт синхронизируется +- **WHEN** разработчик запускает команду синхронизации при доступном backend с валидным OpenAPI-документом +- **THEN** frontend обновляет versioned snapshot и сгенерированные transport types из опубликованного контракта #### Scenario: API-контракт изменяется - **WHEN** backend меняет опубликованный API-контракт -- **THEN** frontend может повторно сгенерировать типы и запросный модуль до интеграции изменения +- **THEN** frontend может синхронизировать snapshot и повторно сгенерировать transport types до интеграции изменения + +#### Scenario: Синхронизация контракта завершается ошибкой +- **WHEN** backend недоступен или публикует невалидный OpenAPI-документ +- **THEN** команда завершается с ошибкой и существующие snapshot и сгенерированные transport types не изменяются ### Requirement: Frontend не зависит от OpenAPI во время работы -Работающий frontend MUST использовать ранее сгенерированный клиент и MUST NOT требовать доступность Swagger или OpenAPI endpoint во время запуска или выполнения пользовательского сценария. +Работающий frontend MUST использовать ранее сгенерированные transport types и MUST NOT требовать доступность Swagger или OpenAPI endpoint во время запуска, выполнения пользовательского сценария или локальной проверки актуальности generated artifact. Доступ к backend OpenAPI endpoint разрешён только команде синхронизации snapshot. #### Scenario: OpenAPI endpoint недоступен после сборки frontend - **WHEN** frontend уже собран, а endpoint OpenAPI временно недоступен - **THEN** frontend продолжает использовать сгенерированный код для запросов к API + +#### Scenario: Локальная проверка выполняется без backend +- **WHEN** разработчик запускает локальный quality gate без доступного backend +- **THEN** проверка актуальности generated artifact использует сохранённый snapshot и не обращается к OpenAPI endpoint + +### Requirement: Frontend обнаруживает дрейф сгенерированного контракта +Frontend SHALL проверять, что committed generated transport types соответствуют versioned OpenAPI snapshot, и MUST завершать quality gate с ошибкой при расхождении. + +#### Scenario: Generated artifact устарел +- **WHEN** OpenAPI snapshot изменён, а generated transport types не обновлены +- **THEN** проверка контракта завершается с ошибкой + +#### Scenario: Generated artifact изменён вручную +- **WHEN** generated transport types отличаются от результата генерации из сохранённого snapshot +- **THEN** проверка контракта завершается с ошибкой