From d9ada2762169b3347a0dc4b7e7ce6c7cd207af76 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Tue, 15 Sep 2026 20:45:37 +0300 Subject: [PATCH] docs: make frontend instructions self-contained --- .ai/architecture.md | 5 +- .ai/conventions/data.md | 21 +++++--- .ai/conventions/engineering.md | 76 +++++++++++++++++++++++++++++ .ai/conventions/testing.md | 4 +- .ai/conventions/typescript-react.md | 2 +- .ai/conventions/ui.md | 12 ++++- AGENTS.md | 36 +++++++------- README.md | 53 ++++++++++++++++---- 8 files changed, 165 insertions(+), 44 deletions(-) create mode 100644 .ai/conventions/engineering.md diff --git a/.ai/architecture.md b/.ai/architecture.md index 1eb45fa..877b55d 100644 --- a/.ai/architecture.md +++ b/.ai/architecture.md @@ -62,9 +62,8 @@ public API `shared/theme`. Storybook показывает synthetic presentation не использует application API. RTL/MSW проверяют поведение приложения отдельно. Existing route-aware AppShell расположен в shared/ui; это legacy placement, -а не образец нового shared primitive. Общий план улучшений читается только -при migration через [router](../AGENTS.md). -При изменении foundation прочитай [design system](../../docs/design/overview.md). +а не образец нового shared primitive. При изменении foundation прочитай +[UI](conventions/ui.md#визуальная-основа) и [Storybook](conventions/storybook.md). ## Примеры diff --git a/.ai/conventions/data.md b/.ai/conventions/data.md index b73d106..d50f3ce 100644 --- a/.ai/conventions/data.md +++ b/.ai/conventions/data.md @@ -28,7 +28,7 @@ client, runtime schema, hash/checksum registry или дополнительны без отдельной текущей потребности; existing OpenAPI flow уже владеет генерацией. Frontend-команды не читают соседний backend checkout и не проверяют backend runtime OpenAPI; изменение wire shape/status/route сохраняет -[границу HTTP-контракта](../../../docs/architecture/http-api.md#источник-контракта). +публичный контракт предоставленного snapshot. Одна UI-модель не требует менять API. `openapi:generate` работает offline от snapshot; `openapi:check` генерирует во временный каталог и сравнивает, не меняя tracked files. @@ -40,18 +40,23 @@ Authorization. Auth endpoints исключены из Bearer/retry middleware. R Не добавляй собственный refresh/retry loop в caller. Protected queries используют existing session-aware boundary; logout очищает -session-scoped cache. При auth/token change читай [security](../../../backend/.ai/conventions/security-auth.md) -и [authentication](../../../docs/domains/authentication/overview.md). +session-scoped cache. Access token хранится только в памяти, refresh передаётся +httpOnly cookie с `credentials: include`. Не сохраняй tokens в localStorage, +URL, логах или fixtures. Logout очищает локальную session/cache и при ошибке +сети; до окончания auth bootstrap показывай neutral state. Перед auth change +прочитай shared client, session owner и их tests. Успешная connection mutation коммитит подтверждённый status и сбрасывает financial GET subtree; ошибка mutation не меняет generation/cache. Это отдельная граница -от обычного фонового refetch: [connection policy](../../../docs/integrations/tbank.md#граница-финансового-кэша). +от обычного фонового refetch. Даже повторное сохранение того же token начинает +новую generation; поздний ответ прежней generation не попадает в новый кэш. Mutations используют существующий generated client и query-key owner; после успеха обновляй/invalidируй только связанные данные. Optimistic update нужен конкретному UX и обязан иметь rollback/error behavior. -Default focus refetch выключен; freshness и retry определяются существующей -[query policy](../../../docs/adr/007-react-query-fetch-policy.md) и feature source. +Default focus refetch выключен; freshness и retry задаются существующими +query options у entity/feature owner. После `staleTime` повторное открытие +экрана может запустить background refetch; focus сам по себе его не запускает. При обычном refetch сохраняй Client Cached Snapshot, при session/connection смене не показывай данные прежнего владельца. UI presentation — в UI convention. @@ -76,5 +81,5 @@ Network/unknown error получает безопасное локальное Изменяя header/retry/invalidation, проверяй наблюдаемое поведение через MSW и [Testing](testing.md); не обращайся к реальному T-Bank. `npm run openapi:check`, `npm run typecheck` и targeted tests проверяют разные -части решения. Для API semantics нужны exact docs и проверки наблюдаемого -HTTP-поведения на стороне backend. +части решения. Для поведения API-клиента используй сценарии MSW на публичной +HTTP-границе; реализация сервера не является входом frontend-проверок. diff --git a/.ai/conventions/engineering.md b/.ai/conventions/engineering.md new file mode 100644 index 0000000..89db9f3 --- /dev/null +++ b/.ai/conventions/engineering.md @@ -0,0 +1,76 @@ +# Работа в репозитории + +## Граница и порядок работы + +Репозиторий сам содержит инструкции, исходники, конфигурацию и входы своих +проверок. Сборка, CI и обязательный маршрут чтения работают без соседних +checkout. Внешний API задаётся предоставленным публичным контрактом; +исходники или инструкции его реализации не являются зависимостью разработки. +При нехватке требования сообщи, какое решение не определено, до его реализации. + +Начни с результата и affected areas. До edits проследи flow/invariants, найди +owner и 1–3 примера. Код показывает механизм; случайный аналог не меняет контракт. +Работай напрямую; OpenSpec/SDD lifecycle — только по явному запросу. +Перед Node-командами активируй местную `.nvmrc`: `nvm use --silent`. +Сохраняй чужие staged/unstaged/untracked изменения и read-only архивы. + +## Change scope + +Сначала расширь существующего владельца. Иначе используй standard library, +платформу или установленную dependency; небольшая новая реализация допустима +при собственной ответственности. Не создавай helper/interface/wrapper ради +возможного reuse. Общее имя не означает общую бизнес-ответственность. +Сохраняй validation, authorization, atomicity, accessibility и migration safety. + +После diff выполни Boy Scout review: проверь новое дублирование, owner, callers, +небольшие связанные legacy issues и соответствие local conventions. Исправляй +непосредственно связанные low-risk проблемы; широкий cleanup — отдельная задача. + +## Domain values + +Finite business vocabulary принадлежит одному domain/module/entity owner. +Переиспользуй его symbols; для нового словаря без аналога — `as const` + union. +Сохраняй wire values; generated/external enums не переписывай вручную. +Numeric policy называй по смыслу и единицам. Индексы, `Math.max(value, 0)` и +обычные labels не выноси в constants без semantic benefit. +Локальный TypeScript/lint профиль определяет дополнительные ограничения. + +## Dependencies + +Проверь аналоги и direct dependencies в местном manifest до добавления пакета. +Новая dependency оправдана текущей потребностью и меньшей общей сложностью; +оцени compatibility, runtime/bundle и maintenance/security. Импортируемый пакет +объявляется напрямую; транзитивная установка не является контрактом. +Сохраняй exact versions и lockfile; воспроизводимая установка — `npm ci`. +Peer-конфликт решай совместимыми версиями без `--force`/`--legacy-peer-deps`. +Используй существующий analyzer; не добавляй второй для той же проверки. + +## Test Intent + +Перед нетривиальным тестом кратко определи наблюдаемый исход, regression risk, +значимые edge/error/concurrency cases и самую дешёвую надёжную boundary. +Отдельный artifact не нужен. Проверяй результат, а не private call order, +декларации, тривиальные wrappers или поведение framework. +При bug сначала воспроизведи исход. Mock race не доказывает DB atomicity. +Уровни и runnable commands выбирай по местной testing convention. + +## Проверки и завершение + +Code/dependency change: `npm ci`, полный `npm run verify` и дополнительные +risk checks из местной testing convention. Targeted tests ускоряют итерацию, +но не заменяют финальный gate. `lint:fix`/format — edits, не proof. +Markdown-only: проверь новые/изменённые тексты, локальные ссылки/якоря, маршруты +чтения и `git diff --check`; application build ради текста не нужен. +Повтори routing по staged/unstaged/new files. Запиши краткие команды, результаты +и ограничения. Зелёный lint не заменяет semantic/security review. + +## Git и релизы + +Работа — в `codex/` от актуального `develop`, затем PR в `develop`. +Сохраняй чужие изменения; перед commit просмотри staged paths и diff. +Commits и PR titles — Conventional Commits; допустим `spec:`. Каждый commit +содержит одну связанную задачу. При связанной issue добавь её номер в конец +заголовка (`fix: … #12`); без issue номер не нужен. Root другого checkout +не управляет этим repo. +Прямой push в `main`, force-push и перенос опубликованных тегов не входят в +обычную работу. Перед release/hotfix прочитай местный README, раздел «Релизы». diff --git a/.ai/conventions/testing.md b/.ai/conventions/testing.md index 8b3513a..740656a 100644 --- a/.ai/conventions/testing.md +++ b/.ai/conventions/testing.md @@ -1,7 +1,7 @@ # Frontend testing Перед нетривиальными тестами сформулируй -[общий Test Intent](../../../.ai/conventions/testing.md); отдельный документ +[общий Test Intent](engineering.md#test-intent); отдельный документ не требуется. Trivial low-risk изменения обходятся без него. ## Выбор проверки @@ -35,7 +35,7 @@ MSW запускается только Vitest setup в `src/test/`, без brow экземпляр и seeded cache. Providers выбирай явно. Existing teardown выполняет RTL cleanup, auth reset и MSW reset: [test client](../../src/test/query.tsx). Тестовый клиент отключает retries запросов и мутаций для предсказуемого исхода; -это не меняет [политику обновления приложения](../../../docs/adr/007-react-query-fetch-policy.md). +это не меняет [политику обновления приложения](data.md#auth-и-invalidation). ## Важные случаи diff --git a/.ai/conventions/typescript-react.md b/.ai/conventions/typescript-react.md index 4dd839c..462ce0c 100644 --- a/.ai/conventions/typescript-react.md +++ b/.ai/conventions/typescript-react.md @@ -32,7 +32,7 @@ Container/VM/view и state owners — в [UI](ui.md). ## Domain literals and magic values Общее правило ownership и символических значений — -[Domain values](../../../.ai/conventions/code-health.md#domain-values). +[Domain values](engineering.md#domain-values). Backend vocabulary бери из generated `paths`, не создавай ручную копию enum. Текущий `schema.d.ts` содержит только types: typed wire literals допустимы, когда generator не предоставляет runtime values. diff --git a/.ai/conventions/ui.md b/.ai/conventions/ui.md index 1f98622..badd161 100644 --- a/.ai/conventions/ui.md +++ b/.ai/conventions/ui.md @@ -18,7 +18,7 @@ components живут у feature/entity/page, даже при повторном Wrapper нужен для проектной ответственности, а не ради нового имени для Button. Локальный CSS module принадлежит component; общий token вводи для -реально общей роли через [design system](../../../docs/design/overview.md). +реально общей роли в [визуальной основе](#визуальная-основа). | Состояние | Владелец | | --- | --- | @@ -104,3 +104,13 @@ Type/style: `npm run typecheck`, `npm run lint`. Для risk behavior/измен используй [Testing](testing.md). UI: keyboard, narrow viewport, loading/error/empty/loaded и recovery по применимости. [Storybook](storybook.md) задаёт выбор stories и изоляцию каталога; сверяй UI с задачей отдельно от build. + +## Визуальная основа + +Общая тема живёт в `src/shared/theme`: `tokens.json` задаёт переносимые значения, +`theme.ts` адаптирует их к Mantine. Используй public API темы и её semantic roles, +без отдельной палитры в page/component. Поддерживается светлая схема. +Цвет дополняет подпись или знак; проверяй контраст конкретной пары text/background. +Warning не используй для мелкого текста на белом. Деньги сохраняют валюту, знак +и различие между нулём и неизвестной суммой. Общие состояния демонстрирует +Storybook; presentation stories используют synthetic data без application API. diff --git a/AGENTS.md b/AGENTS.md index 709069b..7936743 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,9 @@ # Frontend router -Общие инструкции — [workspace AGENTS](../AGENTS.md). Объединяй triggers задачи, -путей и обнаруженных boundaries; читай только нужные темы до решения. -Системные факты/spec/ADR выбирай в разделе [Источники](#источники) по вопросу. +Репозиторий самостоятельный: обязательные инструкции и проверки используют +только его файлы и предоставленные публичные контракты. Перед работой прочитай +[порядок работы](.ai/conventions/engineering.md#граница-и-порядок-работы), затем +объединяй triggers задачи и путей; читай активированные темы до решения. | Решение / сигнал | Читать | | --- | --- | @@ -12,28 +13,25 @@ | Только продуктовая строка / 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#источник-контракта) | +| Login/refresh/logout, token/cookie, session ownership | [Data/auth](.ai/conventions/data.md#auth-и-invalidation) + [Testing](.ai/conventions/testing.md) | +| Request/response field, route/status/error shape, OpenAPI | [Data/contract](.ai/conventions/data.md#типы-и-contract) | | 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) | +| Helper/dependency/новый pattern, refactor/scope | [Change scope](.ai/conventions/engineering.md#change-scope), для пакета — [Dependencies](.ai/conventions/engineering.md#dependencies) | +| Выбор gate, tooling, завершение | [Проверки](.ai/conventions/engineering.md#проверки-и-завершение) | Показ query state не означает изменение fetching. Изменение тестов активирует -Testing и без production diff. Повтори routing по итоговому diff, включая новые -файлы. Migration backlog читается только при работе над соответствующим finding -через [plan](../.ai/migrations/conventions-v1/plan.md). +Testing и без production diff. Перед завершением повтори routing по новым и +изменённым файлам. Для Git/release прочитай +[Git и релизы](.ai/conventions/engineering.md#git-и-релизы). ## Источники -Открывай только источник по возникшему вопросу; примеры кода не переопределяют -контракт. 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) | +| HTTP request/response/errors | [OpenAPI snapshot](src/shared/api/spec/openapi.json), generated types и [Data](.ai/conventions/data.md) | +| Auth/session/refresh/cache | [Data](.ai/conventions/data.md#auth-и-invalidation), source и colocated tests владельца | +| Money, account/position/income | Entity types, API snapshot, formatters и tests соответствующего slice | +| Theme/tokens, UI-каталог | [UI](.ai/conventions/ui.md), [Storybook](README.md#ui-каталог-storybook) | +| Поведение области | Source и tests её page/entity/feature; неясное ожидание уточни до изменения | | Versions/scripts/check scope | Manifest/lockfile, Biome/TypeScript/dependency configs | diff --git a/README.md b/README.md index 661e561..805f09d 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ document и затем выполните `npm run openapi:generate`. `npm run build:catalog`. Каталог использует общую тему приложения, вымышленные данные и локальные действия без application API. Примеры не доказывают восстановление реального запроса. Визуальные роли описаны в -[дизайн-системе](../docs/design/overview.md). +[UI convention](.ai/conventions/ui.md#визуальная-основа). ## Документация @@ -65,16 +65,49 @@ document и затем выполните `npm run openapi:generate`. - [Data/API/cache](.ai/conventions/data.md) - [Testing](.ai/conventions/testing.md) - [UI-каталог Storybook](#ui-каталог-storybook) -- [Обзор поставленной системы](../docs/overview.md) -- [Карта проектных фактов](../docs/README.md) -Runtime OpenAPI backend сверяется с frontend snapshot отдельной read-only -backend командой `npm run test:openapi-contract`. -Разработка идёт напрямую по задаче пользователя. Каждый affected repo проходит -свой verify; при изменении wire contract дополнительно выполняется contract check. -Обязательного OpenSpec/SDD lifecycle нет. - -Версии и ветки: [процесс релиза](../README.md#релизы-и-ветки). +Checkout frontend содержит все входы сборки и проверок. Публичная схема API +поступает как отдельный artifact; команды frontend используют только её +локальную копию и не обращаются к исходникам или CI сервера. Доставка новой +схемы — явное обновление snapshot с review и `npm run openapi:generate`. +Runtime URL API задаётся окружением, а не расположением другого репозитория. +Разработка идёт напрямую по задаче пользователя; OpenSpec/SDD — по явному запросу. Knip исключает `conventional-changelog-conventionalcommits`: release-it загружает preset по имени из `.release-it.json`. Версия preset закреплена напрямую. + +## Релизы + +Репозиторий выпускается независимо. `develop` — интеграция, `main` — принятые +релизы. Работа: `codex/` от актуального `develop`, PR в `develop`. +Обычные PR допускают squash; release и обратная синхронизация сохраняют историю. +Commits и PR titles — Conventional Commits, включая `spec:`. Версии вручную не повышай. + +1. Получи `main`, `develop` и теги: `git fetch origin --tags`. Убедись, что + `develop` содержит последний релиз. Создай `release/` от проверенного + `develop`; `nvm use --silent`, `npm ci`. Hotfix начинается от `main` в + `release/hotfix-` и проходит тот же процесс. +2. На чистой ветке выполни `npm run release:preview`, просмотри версию/changelog, + затем `npm run release:prepare`. Команда создаёт `chore(release): X.Y.Z`, + обновляет manifest/lockfile/CHANGELOG; тег и push не выполняет. Не обходи + guards ветки, чистого дерева, отсутствующих тегов или уже подготовленной версии. +3. Проверь diff и штатные gates этого репозитория. Отправь только release-ветку, + открой PR в `main` с заголовком `chore(release): X.Y.Z`. После зелёных + `verify`, `security`, `commits` выполни fast-forward-only merge. Если `main` + изменился, согласуй историю и повтори проверки кандидата. +4. Получи удалённый `main`, сравни его HEAD с проверенным release SHA и версией + manifest/lockfile. Создай аннотированный `vX.Y.Z` именно на этом SHA: + `git tag -a vX.Y.Z -m "Release X.Y.Z"`, затем отправь только + `git push origin refs/tags/vX.Y.Z`. Не запускай release-it повторно на `main`. +5. Верни release-коммит через PR `main → develop` без squash/rebase; проверь, + что `develop` содержит тег. Опубликованные теги не перемещай. При сбое после + merge создай отсутствующий тег без нового bump; до merge исправь и проверь + существующий кандидат без повторного повышения версии. + +`feat:` повышает minor, `fix:`/`perf:` и прочие разрешённые типы — patch; +`!`/`BREAKING CHANGE:` — major, включая версии до 1.0. `chore(release):` +исключён из рекомендации и changelog. Конфигурация — `.release-it.json`. +В Gitea для `main`/`develop` должны быть запрещены прямой/force push и обход +администратором, обязательны `verify`, `security`, `commits`; теги `v*` защищены. +Workflow сам не включает эти настройки. Отдельное review approval для одного +владельца не требуется. Npm publishing, deploy и Gitea Releases не включены.