From 9f7e342333b80bfacfd5bc394a2dcd0613d33ff3 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Tue, 15 Sep 2026 05:58:31 +0300 Subject: [PATCH] docs: simplify conventions layout --- .ai/CONVENTIONS.md | 9 --- .ai/SOURCES.md | 18 ----- .ai/architecture.md | 66 ++++++++++++++++--- .ai/conventions/{frontend-data.md => data.md} | 2 +- .ai/conventions/frontend-architecture.md | 55 ---------------- .ai/conventions/storybook.md | 2 +- .../{frontend-testing.md => testing.md} | 0 .ai/conventions/typescript-react.md | 8 +-- .ai/conventions/{frontend-ui.md => ui.md} | 4 +- AGENTS.md | 33 +++++++--- README.md | 10 +-- 11 files changed, 93 insertions(+), 114 deletions(-) delete mode 100644 .ai/CONVENTIONS.md delete mode 100644 .ai/SOURCES.md rename .ai/conventions/{frontend-data.md => data.md} (98%) delete mode 100644 .ai/conventions/frontend-architecture.md rename .ai/conventions/{frontend-testing.md => testing.md} (100%) rename .ai/conventions/{frontend-ui.md => ui.md} (98%) diff --git a/.ai/CONVENTIONS.md b/.ai/CONVENTIONS.md deleted file mode 100644 index 8dec1c0..0000000 --- a/.ai/CONVENTIONS.md +++ /dev/null @@ -1,9 +0,0 @@ -# Frontend conventions: вход - -Актуальный routing по задаче, пути и boundary находится в [AGENTS](../AGENTS.md). -Выбирай только активированные темы; при новой границе повтори routing. -[Architecture](architecture.md) объясняет mental model, [SOURCES](SOURCES.md) -ведёт к системным фактам, specifications и ADR по конкретному вопросу. - -Этот адрес сохранён для существующих входящих ссылок; второй копии правил -и таблицы маршрутов здесь нет. diff --git a/.ai/SOURCES.md b/.ai/SOURCES.md deleted file mode 100644 index 63570fd..0000000 --- a/.ai/SOURCES.md +++ /dev/null @@ -1,18 +0,0 @@ -# Источники frontend - -| Что нужно установить | Владелец | -| --- | --- | -| Какое implementation-решение принять | [AGENTS](../AGENTS.md) | -| HTTP wire contract и направление генерации | Backend-owned contract → provided snapshot → [Источник HTTP-контракта](../../docs/architecture/http-api.md#источник-контракта) | -| Error codes/validation/error envelope | [docs: HTTP API](../../docs/architecture/http-api.md) | -| Auth/session, token storage и refresh | [Аутентификация](../../docs/domains/authentication/overview.md), [подключение брокера](../../docs/integrations/tbank.md) | -| Значение Money, cached snapshot, account/position/income | [docs: Domain](../../docs/glossary.md) | -| Mantine/theme/tokens и существующие stories | [Дизайн-система](../../docs/design/overview.md), [UI-каталог](../README.md#ui-каталог-storybook) | -| As-built behavior нужной feature | [docs: Features](../../docs/README.md#предметные-области) | -| Существующие behavioral requirements, если нужны по задаче | [specs index](../../specs/AI_INDEX.md) | -| Действующие versions/scripts/check scope | package.json, lockfile, Biome/TS/dependency configs | - -Открывай точный source только по возникшему вопросу. Code examples показывают -pattern, но не переопределяют контракт. Specs не являются editable OpenAPI; -SDD-процедура для обычной работы не нужна. При конфликте источников сначала -установи факты, не меняй generated types или security policy по догадке. diff --git a/.ai/architecture.md b/.ai/architecture.md index 12bb7b3..1eb45fa 100644 --- a/.ai/architecture.md +++ b/.ai/architecture.md @@ -1,4 +1,6 @@ -# Frontend mental model +# Frontend architecture + +## Устройство и data flow React + TypeScript + Vite SPA. Router задаёт navigation; providers в `app` подключают auth, TanStack Query, i18n и Mantine. Vite использует React Compiler; @@ -12,17 +14,47 @@ page container → page VM → entity/feature query → shared/api → backend / props → state/section views ``` -`widgets` не используется. Pages владеют route composition и локальным поведением; -features — самостоятельными возможностями пользователя; entities — устойчивыми -доменными понятиями; shared — техническими primitives без domain responsibility. -Правила выделения owner и public `index.ts` — в -[FSD convention](conventions/frontend-architecture.md). - Server state и lifecycle запроса принадлежат TanStack Query; VM собирает данные для view. Transport — `openapi-fetch`/`openapi-react-query`, типизированные из предоставленного snapshot через `openapi-typescript`. Backend владеет wire contract; frontend не читает его checkout для генерации и не вызывает T-Bank. -[Data convention](conventions/frontend-data.md) задаёт auth/cache границы. +[Data convention](conventions/data.md) задаёт auth/cache границы. + +## Выбор владельца + +Сначала найди владельца поведения и ближайший пример. Расширяй existing slice, +если ответственность остаётся связной. Размер файла или возможный будущий reuse +не создают новую архитектурную границу. + +| Ответственность | Размещение | +| --- | --- | +| Bootstrap, глобальные providers, routes | `app` | +| Композиция маршрута и поведение, нужное только этому экрану | `pages/` | +| Самостоятельная пользовательская возможность с собственным смыслом | `features/` | +| Представление/поведение бизнес-сущности | `entities/` | +| Переиспользуемый код без WealthLedger domain meaning | `shared` | + +Локальный FSD profile: `app → pages → features → entities → shared`, без `widgets`. +Крупный уникальный блок остаётся в своей page. Form, mutation или click handler +сами по себе не требуют feature. Два callers не делают domain helper generic shared. +Slice именуется по ответственности, а не `helpers`, `hooks` или `types`. + +**Безопасный путь:** при сомнении оставь код у ближайшего допустимого owner. +Извлекай его, когда появилась самостоятельная ответственность или реальный reuse. +Не вводи новый layer и не обходи public API, чтобы устранить неудобный import. + +## Imports и public API + +Cross-slice consumer использует `@/` и `index.ts` владельца, например +`@/entities/portfolio`. Alias Vite и TypeScript согласован. Другой slice не +импортирует внутренний файл напрямую. Slices `pages`, `features`, `entities` не зависят от соседнего slice своего слоя; +композиция принадлежит вышележащему owner. Existing меж-slice исключения +не являются разрешением добавлять новые. + +Направление зависимостей и public imports частично проверяет +`npm run deps`; Knip проверяет использование exports, а не смысл placement. + +## UI foundation `shared/theme/tokens.json` — переносимый source design tokens; `theme.ts` адаптирует доверенный JSON к Mantine без runtime schema. App и Storybook читают @@ -31,4 +63,20 @@ public API `shared/theme`. Storybook показывает synthetic presentation Existing route-aware AppShell расположен в shared/ui; это legacy placement, а не образец нового shared primitive. Общий план улучшений читается только -при migration через [router](CONVENTIONS.md). +при migration через [router](../AGENTS.md). +При изменении foundation прочитай [design system](../../docs/design/overview.md). + +## Примеры + +- Общая busy-область: существующий [LoadingSkeleton](../src/shared/ui/LoadingSkeleton.tsx), а не domain widget в shared. +- Account-income state views: [страница](../src/pages/account-income/ui/AccountIncomePage.tsx) и её flat `components/*.view.tsx`; новый page-local filter остаётся здесь. +- Portfolio query: [entity public API](../src/entities/portfolio/index.ts); новая UI-сводка с бизнес-смыслом не становится shared только из-за нескольких экранов. + +## Проверка + +После placement/import change: `npm run deps`, `npm run typecheck`, `npm run knip`; +после собственного behavior — применимые tests. Финальный gate указан в AGENTS. +Existing dependency config не запрещает все same-layer imports: их и смысл +owner проверяет review. Не объявляй Steiger установленным; graph проверяет +текущий dependency-cruiser. Review отдельно проверяет смысл owner: разрешённый dependency graph не доказывает +правильный выбор слоя. diff --git a/.ai/conventions/frontend-data.md b/.ai/conventions/data.md similarity index 98% rename from .ai/conventions/frontend-data.md rename to .ai/conventions/data.md index 1764a00..b73d106 100644 --- a/.ai/conventions/frontend-data.md +++ b/.ai/conventions/data.md @@ -74,7 +74,7 @@ Network/unknown error получает безопасное локальное [usePortfolio](../../src/entities/portfolio/api/usePortfolio.ts) — entity query; [shared client](../../src/shared/api/client.ts) — transport/auth boundary. Изменяя header/retry/invalidation, проверяй наблюдаемое поведение через MSW и -[Testing](frontend-testing.md); не обращайся к реальному T-Bank. +[Testing](testing.md); не обращайся к реальному T-Bank. `npm run openapi:check`, `npm run typecheck` и targeted tests проверяют разные части решения. Для API semantics нужны exact docs и проверки наблюдаемого HTTP-поведения на стороне backend. diff --git a/.ai/conventions/frontend-architecture.md b/.ai/conventions/frontend-architecture.md deleted file mode 100644 index 5983689..0000000 --- a/.ai/conventions/frontend-architecture.md +++ /dev/null @@ -1,55 +0,0 @@ -# Frontend architecture - -## Выбор владельца - -Сначала найди владельца поведения и ближайший пример. Расширяй existing slice, -если ответственность остаётся связной. Размер файла или возможный будущий reuse -не создают новую архитектурную границу. - -| Ответственность | Размещение | -| --- | --- | -| Bootstrap, глобальные providers, routes | `app` | -| Композиция маршрута и поведение, нужное только этому экрану | `pages/` | -| Самостоятельная пользовательская возможность с собственным смыслом | `features/` | -| Представление/поведение бизнес-сущности | `entities/` | -| Переиспользуемый код без WealthLedger domain meaning | `shared` | - -Локальный FSD profile: `app → pages → features → entities → shared`, без `widgets`. -Крупный уникальный блок остаётся в своей page. Form, mutation или click handler -сами по себе не требуют feature. Два callers не делают domain helper generic shared. -Slice именуется по ответственности, а не `helpers`, `hooks` или `types`. - -**Безопасный путь:** при сомнении оставь код у ближайшего допустимого owner. -Извлекай его, когда появилась самостоятельная ответственность или реальный reuse. -Не вводи новый layer и не обходи public API, чтобы устранить неудобный import. - -## Imports и public API - -Cross-slice consumer использует `@/` и `index.ts` владельца, например -`@/entities/portfolio`. Alias Vite и TypeScript согласован. Другой slice не -импортирует внутренний файл напрямую. Slices `pages`, `features`, `entities` не зависят от соседнего slice своего слоя; -композиция принадлежит вышележащему owner. Existing меж-slice исключения -не являются разрешением добавлять новые. - -Направление зависимостей и public imports частично проверяет -`npm run deps`; Knip проверяет использование exports, а не смысл placement. - -## UI foundation - -Mental model, theme и data flow — в [architecture](../architecture.md). -При изменении foundation прочитай [design system](../../../docs/design/overview.md). - -## Примеры - -- Общая busy-область: существующий [LoadingSkeleton](../../src/shared/ui/LoadingSkeleton.tsx), а не domain widget в shared. -- Account-income state views: [страница](../../src/pages/account-income/ui/AccountIncomePage.tsx) и её flat `components/*.view.tsx`; новый page-local filter остаётся здесь. -- Portfolio query: [entity public API](../../src/entities/portfolio/index.ts); новая UI-сводка с бизнес-смыслом не становится shared только из-за нескольких экранов. - -## Проверка - -После placement/import change: `npm run deps`, `npm run typecheck`, `npm run knip`; -после собственного behavior — применимые tests. Финальный gate указан в AGENTS. -Existing dependency config не запрещает все same-layer imports: их и смысл -owner проверяет review. Не объявляй Steiger установленным; graph проверяет -текущий dependency-cruiser. Review отдельно проверяет смысл owner: разрешённый dependency graph не доказывает -правильный выбор слоя. diff --git a/.ai/conventions/storybook.md b/.ai/conventions/storybook.md index c82e9d0..62fb6b9 100644 --- a/.ai/conventions/storybook.md +++ b/.ai/conventions/storybook.md @@ -29,5 +29,5 @@ RTL suite и не тестируй внутренности Mantine. Сущес `npm run typecheck`, `npm run deps:storybook`, `npm run build:catalog` проверяют типы, изоляцию и сборку. Build не выполняет `play` как CI interaction suite; для проверки interaction открой story в браузере, для regression используй -существующий [Testing](frontend-testing.md). Визуальное соответствие задаче, +существующий [Testing](testing.md). Визуальное соответствие задаче, keyboard и узкий viewport проверяются отдельно от успешной сборки. diff --git a/.ai/conventions/frontend-testing.md b/.ai/conventions/testing.md similarity index 100% rename from .ai/conventions/frontend-testing.md rename to .ai/conventions/testing.md diff --git a/.ai/conventions/typescript-react.md b/.ai/conventions/typescript-react.md index fee0393..4dd839c 100644 --- a/.ai/conventions/typescript-react.md +++ b/.ai/conventions/typescript-react.md @@ -27,7 +27,7 @@ Hooks вызываются без условий и ранних выходов контракту стабильной identity; не добавляй их ко всем значениям/handlers. React Compiler включён в Vite, но не доказывает выигрыш и не заменяет effect correctness. Не удаляй существующую memoization без понимания её consumers. -Container/VM/view и state owners — в [UI](frontend-ui.md). +Container/VM/view и state owners — в [UI](ui.md). ## Domain literals and magic values @@ -39,15 +39,15 @@ Backend vocabulary бери из generated `paths`, не создавай руч `ProblemDetailsDto.code` сейчас `string`. Не объявляй закрытый frontend error enum и не превращай неизвестный code в известный через cast. Повторяющуюся -обработку своди у ответственного owner; [errors](frontend-data.md#errors). +обработку своди у ответственного owner; [errors](data.md#errors). `noMagicNumbers` в Biome — error; fixtures/tests/stories исключены только из этого правила. Policy получает имя и единицы; подавление допустимо для incidental value с объяснением. Обычный UI-текст не превращай в constant: -его owner — [i18n](frontend-ui.md#строки-и-валидация). +его owner — [i18n](ui.md#строки-и-валидация). Semantic magic strings и props profile требуют review. ## Проверка `npm run typecheck` и `npm run lint`; behavior проверяй по -[Testing](frontend-testing.md), placement — по [FSD](frontend-architecture.md). +[Testing](testing.md), placement — по [FSD](../architecture.md). diff --git a/.ai/conventions/frontend-ui.md b/.ai/conventions/ui.md similarity index 98% rename from .ai/conventions/frontend-ui.md rename to .ai/conventions/ui.md index f1c13f4..1f98622 100644 --- a/.ai/conventions/frontend-ui.md +++ b/.ai/conventions/ui.md @@ -22,7 +22,7 @@ Wrapper нужен для проектной ответственности, а | Состояние | Владелец | | --- | --- | -| Ответ сервера / query lifecycle | TanStack Query, [data rules](frontend-data.md) | +| Ответ сервера / query lifecycle | TanStack Query, [data rules](data.md) | | Ввод и ошибки формы | React Hook Form + Zod, container/VM | | Параметр навигации, который должен переживать переход/ссылку | Router/URL | | Локальное открытие, focus, rendering fallback | React UI state | @@ -101,6 +101,6 @@ Types, props, hooks и memoization — в [TypeScript/React](typescript-react.md ## Проверка Type/style: `npm run typecheck`, `npm run lint`. Для risk behavior/изменения тестов -используй [Testing](frontend-testing.md). UI: keyboard, narrow viewport, +используй [Testing](testing.md). UI: keyboard, narrow viewport, loading/error/empty/loaded и recovery по применимости. [Storybook](storybook.md) задаёт выбор stories и изоляцию каталога; сверяй UI с задачей отдельно от build. diff --git a/AGENTS.md b/AGENTS.md index 3d9dff9..709069b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,21 +2,20 @@ Общие инструкции — [workspace AGENTS](../AGENTS.md). Объединяй triggers задачи, путей и обнаруженных boundaries; читай только нужные темы до решения. -[Sources](.ai/SOURCES.md) выбирает факты/spec/ADR по возникшему вопросу. +Системные факты/spec/ADR выбирай в разделе [Источники](#источники) по вопросу. | Решение / сигнал | Читать | | --- | --- | -| Первое знакомство, data flow, общая архитектура | [Mental model](.ai/architecture.md) | -| Создать/перенести component/module/slice, owner/FSD, cross-slice imports | [FSD](.ai/conventions/frontend-architecture.md) | +| Устройство/data flow, component/module/slice, owner/FSD, cross-slice imports | [Architecture](.ai/architecture.md) | | TS types/props/imports/exports, hooks/effects, derived state, memoization, domain literals | [TypeScript/React](.ai/conventions/typescript-react.md) | -| UI/Mantine, form, container/VM/view, state owner, loading/error/recovery | [UI](.ai/conventions/frontend-ui.md) | -| Только продуктовая строка / Zod message | [Строки](.ai/conventions/frontend-ui.md#строки-и-валидация) | -| Query/mutation, server cache, shared/api, generated types | [Data](.ai/conventions/frontend-data.md) | -| API/network/application errors, codes и сообщения | [Errors](.ai/conventions/frontend-data.md#errors) | -| Login/refresh/logout, token/cookie, session ownership | Data + [Security](../backend/.ai/conventions/security-auth.md) + [Testing](.ai/conventions/frontend-testing.md) | +| UI/Mantine, form, container/VM/view, state owner, loading/error/recovery | [UI](.ai/conventions/ui.md) | +| Только продуктовая строка / Zod message | [Строки](.ai/conventions/ui.md#строки-и-валидация) | +| Query/mutation, server cache, shared/api, generated types | [Data](.ai/conventions/data.md) | +| API/network/application errors, codes и сообщения | [Errors](.ai/conventions/data.md#errors) | +| Login/refresh/logout, token/cookie, session ownership | Data + [Security](../backend/.ai/conventions/security-auth.md) + [Testing](.ai/conventions/testing.md) | | Request/response field, route/status/error shape, OpenAPI | Data + [HTTP contract source](../docs/architecture/http-api.md#источник-контракта) | -| Tests или behavior с regression risk | [Testing](.ai/conventions/frontend-testing.md) | -| Storybook/stories/play, theme/tokens/shared UI | [Storybook](.ai/conventions/storybook.md), UI; design facts — через Sources | +| Tests или behavior с regression risk | [Testing](.ai/conventions/testing.md) | +| Storybook/stories/play, theme/tokens/shared UI | [Storybook](.ai/conventions/storybook.md), UI; design facts — в разделе Источники | | Helper/dependency/новый pattern, refactor/scope | [Code health](../.ai/conventions/code-health.md), для пакета — [Dependencies](../.ai/conventions/dependencies.md) | | Выбор gate, tooling, завершение | [Guardrails](../.ai/conventions/guardrails.md) | @@ -24,3 +23,17 @@ Testing и без production diff. Повтори routing по итоговому diff, включая новые файлы. Migration backlog читается только при работе над соответствующим finding через [plan](../.ai/migrations/conventions-v1/plan.md). + +## Источники + +Открывай только источник по возникшему вопросу; примеры кода не переопределяют +контракт. Specs задают behavior, а editable OpenAPI принадлежит backend. + +| Вопрос | Источник | +| --- | --- | +| HTTP contract, validation/error codes | [HTTP API](../docs/architecture/http-api.md) | +| Auth/session/refresh, подключение брокера | [Authentication](../docs/domains/authentication/overview.md), [T-Bank](../docs/integrations/tbank.md) | +| Money, account/position/income/cache meanings | [Глоссарий](../docs/glossary.md) | +| Theme/tokens, UI-каталог | [Дизайн-система](../docs/design/overview.md), [Storybook](README.md#ui-каталог-storybook) | +| Поведение области / требования | [Области docs](../docs/README.md#предметные-области), [Specs index](../specs/AI_INDEX.md) | +| Versions/scripts/check scope | Manifest/lockfile, Biome/TypeScript/dependency configs | diff --git a/README.md b/README.md index ae92092..661e561 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,11 @@ document и затем выполните `npm run openapi:generate`. ## Документация -- [Router conventions](.ai/CONVENTIONS.md) и [источники](.ai/SOURCES.md) -- [Выбор owner/FSD](.ai/conventions/frontend-architecture.md) -- [UI, state, формы и компоненты](.ai/conventions/frontend-ui.md) -- [Data/API/cache](.ai/conventions/frontend-data.md) -- [Testing](.ai/conventions/frontend-testing.md) +- [AGENTS: правила и источники](AGENTS.md) +- [Выбор owner/FSD](.ai/architecture.md) +- [UI, state, формы и компоненты](.ai/conventions/ui.md) +- [Data/API/cache](.ai/conventions/data.md) +- [Testing](.ai/conventions/testing.md) - [UI-каталог Storybook](#ui-каталог-storybook) - [Обзор поставленной системы](../docs/overview.md) - [Карта проектных фактов](../docs/README.md)