diff --git a/.ai/conventions/code-health.md b/.ai/conventions/code-health.md new file mode 100644 index 0000000..a20abcf --- /dev/null +++ b/.ai/conventions/code-health.md @@ -0,0 +1,68 @@ +# Code health и границы изменения + +## Search → reuse → converge + +Перед utility, hook, wrapper, dependency или новым technical pattern: + +1. Проследи поведение, инварианты и callers у ответственного owner. +2. Ищи аналоги по имени **и реализации** (`rg` по imports, API, выражению). +3. Расширь подходящий owner/helper/pattern; выбирай минимальное решение, + полностью закрывающее текущую задачу. Новый файл сам по себе не улучшение. + +Уменьшай количество способов решать одну техническую задачу. Во frontend +сравни query keys, formatters, class composition; в backend — pagination, +error mapping, repositories и обращения к одному provider. Сходный синтаксис +без общей ответственности не требует DRY. Критерий dependency — в +[dependencies](dependencies.md). + +## Change scope + +Consistency с подходящим project pattern предпочтительнее универсальности. +Новые layers, wrappers, generic helpers, factories, schemas, validators, scripts +и дополнительные проверки требуют текущей ответственности или риска. +Сначала используй native API/установленный stack; не строй на гипотетических +consumers и будущих requirements. + +Backend: CQRS, Event Sourcing, Domain Events, Unit of Work, Command Bus, +generic repositories, abstract factories, custom DI и elaborate domain layers +не вводятся только ради «best practice». Frontend: wrapper над Mantine, global +store и дополнительная OpenAPI hash/schema система не создаются автоматически. +Trust-boundary validation, authorization, atomicity, accessibility и необходимая +regression proof остаются частью полного решения. + +## Boy Scout review + +После изменения проверь affected area: новое дублирование, пропущенный helper, +competing patterns, небольшие связанные legacy issues и conventions. +Исправляй в рамках задачи только непосредственно связанные, небольшие, +low-risk, behavior-preserving проблемы, которые легко проверить. +Более широкие находки сохраняй для отдельной migration; массовый cleanup +не является условием завершения текущей задачи. + +## Persist decisions и ratchet + +Устойчивый pattern должен находиться через код и manifest. Добавляй convention, +только если следующей сессии всё ещё нужно явное project decision rule. +У каждого правила один owner; AGENTS содержит trigger и ссылку. +Mental model живёт в `.ai/architecture.md`, intent — в specs, системные знания — +в docs. Audit/backlog не помещай в conventions. + +Legacy фиксируется отдельно; новый diff не добавляет нарушений. При migration +закрывай подтверждённые baseline findings с evidence, сохраняя область проверки. +Зелёный lint не доказывает отсутствие semantic debt. Работа с baseline начинается +с [migration plan](../migrations/conventions-v1/plan.md), только когда задача +затрагивает migration или найденный там owner. + +## Domain values + +Finite business vocabulary (status, error code, operation kind, role) принадлежит +domain/module/entity owner. Перед определением найди существующие types/values; +в comparisons используй символ владельца, без конкурирующих словарей. +Для нового project-owned vocabulary без аналога: `as const` + derived union. +Сохраняй wire values; generated/external enums не переписывай. + +Numeric policy называй по смыслу и единицам (`TIMEOUT_MS`, `PAGE_SIZE`). +Индексы, `index + 1`, `Math.max(value, 0)` и обычные labels/log messages +не выноси в constants без semantic benefit. Локальный lint/i18n profile всё равно +применим. Не создавай tests существования констант или custom magic-string analyzer. +Различия TS/генератора принадлежат local conventions frontend/backend. diff --git a/.ai/conventions/dependencies.md b/.ai/conventions/dependencies.md new file mode 100644 index 0000000..861c5fd --- /dev/null +++ b/.ai/conventions/dependencies.md @@ -0,0 +1,33 @@ +# Dependencies + +## Перед добавлением + +1. Найди аналог по назначению и реализации в affected repo. +2. Проверь его direct dependencies, текущий stack и native/stdlib API. +3. Определи масштаб: один простой случай или повторяющаяся техническая задача. +4. Используй существующее решение, если оно полностью покрывает поведение. + Новый пакет оправдан меньшей общей сложностью, а не сокращением одной строки. + +Повтор non-business задачи в примерно 3+ независимых местах — сигнал пересмотреть +pattern, а не требование немедленно извлечь abstraction. Сравни общий helper, +native API и зрелую небольшую dependency: parsing, serialization, dates, +debounce, class composition. Три `Array.map` не требуют utility library. +Не продолжай копировать boilerplate, если один принятый способ дешевле сопровождать. + +`clsx` сейчас не принят как direct dependency. Разные способы соединить классы +сначала требуют подтверждённого повторения; устанавливать его заранее не нужно. + +## При выборе пакета + +- Укажи конкретную потребность, альтернативу из текущего stack и существенную + цену: совместимость, browser bundle/server runtime, maintenance/security. +- Импортируемый пакет объявляй напрямую в repo владельца; наличие в соседнем + repo или транзитивном дереве не является dependency contract. +- Сохраняй exact versions и lockfile. Перед Node-командами активируй `.nvmrc` + через `nvm use --silent`; воспроизводимая установка — `npm ci` в нужном repo. +- Peer-конфликты решай совместимыми версиями, без `--force`/`--legacy-peer-deps`. + Известная frontend граница TypeScript/openapi-typescript — в + [README](../../frontend/README.md#проверка); версии принадлежат manifest/lockfile. +- После изменения запускай relevant install/type/build/behavior checks и + existing security workflow. Не добавляй второй analyzer, когда задача решается + настройкой существующего; [карта проверок](guardrails.md). diff --git a/.ai/conventions/git.md b/.ai/conventions/git.md new file mode 100644 index 0000000..9293a24 --- /dev/null +++ b/.ai/conventions/git.md @@ -0,0 +1,22 @@ +# Git в workspace + +- Выполняй Git-команды из репозитория владельца: root, frontend, backend, + docs и specs имеют независимые history, index и branches. +- Новая работа: ветка `codex/` от актуального `develop`, затем PR в `develop`. + Сохраняй чужие staged/unstaged/untracked изменения; не переноси их в свой commit. +- Commit содержит одну связанную задачу и её необходимые проверки/документацию. + Межрепозиторное изменение оформляй отдельными commits/PR с взаимными ссылками. +- Commits и заголовки PR — Conventional Commits; допустим `spec:`. + Во frontend номер связанной issue добавляй в конец заголовка (`fix: … #12`); + без связанной issue номер не нужен. Формат проверяет local commitlint. +- Перед commit проверь `git diff --cached --name-status` и сам staged diff + в каждом affected repo. Root tracking ограничен [.gitignore](../../.gitignore): + новый общий файл получает точечный allowlist и проверку + [tracking](../../test-workspace-tracking.py). `git add -f` не используется. +- Не выполняй `git clean -fdx`, reset чужих изменений, force-push, удаление или + перенос опубликованных тегов как обычную очистку. Для опасной операции нужны + явная необходимость, сохранение данных и соответствующая авторизация. +- Обычный PR допускает squash. Release/hotfix, синхронизация main/develop, + release-конфигурация и CI refs требуют чтения + [процесса релиза](../../README.md#релизы-и-ветки): он владеет merge policy, + версиями и тегами. Прямой push в `main` запрещён; версии вручную не повышай. diff --git a/.ai/conventions/guardrails.md b/.ai/conventions/guardrails.md new file mode 100644 index 0000000..a4fd3ca --- /dev/null +++ b/.ai/conventions/guardrails.md @@ -0,0 +1,48 @@ +# Conventions → deterministic guardrails + +Читай при выборе проверки или изменении tooling. Команды выполняются из repo +владельца после `nvm use --silent`. Config/manifest задаёт фактический scope; +таблица не заменяет semantic review и не утверждает, что все checks уже запущены. + +| Convention | Существующий check | Достаточность / следующий шаг | +| --- | --- | --- | +| Git multi-repo tracking | root `python3 test-workspace-tracking.py`, `.gitignore` | Проверяет exact allowlist и сохранность fictional data/nested Git; обновлять вместе с allowlist | +| Conventional Commits | local commitlint + commits workflow | Syntax покрыт; атомарность и scope — review | +| Dependencies | exact manifest/lockfile, `npm ci`, security workflows (audit/Gitleaks) | Reuse, peer compatibility, ценность dependency — review; новый analyzer не нужен | +| Frontend types/style/literals | TypeScript strict, `erasableSyntaxOnly`; Biome `npm run lint` | Props profile, i18n и symbolic strings требуют review; literals exclusions — в Biome | +| Frontend FSD | dependency-cruiser `npm run deps` | Upward imports, cycles, public entry points покрыты; same-layer запрет неполный, semantic ownership не проверяется | +| Unused frontend code/deps | `npm run knip`, TS unused locals/params | Existing tool достаточно; не добавлять второй unused analyzer | +| API generation | frontend `openapi:check`; backend `test:openapi-contract` | Первый сравнивает generated types со snapshot; второй — Nest schema со snapshot. Ни один не доказывает runtime validation errors | +| Query/auth/error/finance behavior | Vitest + RTL/MSW; backend Jest/SQLite/HTTP | Test Intent выбирает риск и уровень, без автоматического теста для каждого файла | +| Storybook | `typecheck`, `deps:storybook`, `build:catalog` | Imports/types/build покрыты; `play`, visual/keyboard/browser behavior проверять отдельно | +| Backend types/style | ESLint/Prettier, app/test TypeScript | Zero-warning gate; `no-explicit-any` отключён, часть unsafe rules включена. Не обещать полного запрета `any` | +| Backend modules/domain | TypeScript и semantic review | Отдельного architecture analyzer нет. При повторных нарушениях сначала рассмотреть existing ESLint `no-restricted-imports`, без нового custom analyzer | +| DB ownership/atomicity | Prisma schema/constraints, `test:sqlite`, `test:e2e`, `test:durability` | Mock Prisma недостаточно. Relevant suites на disposable DB, без reset пользовательской базы | +| Logging/security | Logging/auth/access tests, configured HTTP tests, Gitleaks | Allowlist/redaction имеют границы. Встроенный ESLint `no-console` можно включить отдельно; semantic security review обязателен | +| Markdown/routing | docs markdownlint-cli2 + relative-links, `git diff --check` | Existing docs gate охватывает docs, а не соседние `.ai`; для них запустить тот же установленный CLI с явным списком путей и docs config | + +## Ratchet без новой инфраструктуры + +- Existing error gates остаются обязательными; не ослабляй их ради legacy. +- До расширения FSD rule получи candidate violations существующим depcruise. + Его `--ignore-known` допускает native baseline; сначала проверь формат через + `--help`. Не игнорируй целые directories. Обновление baseline принимается + только с evidence закрытия/явным обоснованием нового исключения. +- Semantic findings учитываются stable IDs в + [inventory](../migrations/conventions-v1/audit.md) и закрываются vertical slices + по [plan](../migrations/conventions-v1/plan.md). Это reviewed inventory, + не автоматический полный счётчик нарушений. +- Новый analyzer/custom baseline CI не создаётся. Если существующей проверки + недостаточно, сначала сформулируй повторяющийся regression и стоимость + поддержки предлагаемого rule. + +## Выбор финального gate + +Markdown/routing-only: проверить все новые/изменённые Markdown, ссылки/якоря, +достижимость тем, смысл и `git diff --check`. Root allowlist change дополнительно +требует tracking-тест. Не запускать application builds/coverage только ради текста. + +Code/dependency changes: full `npm run verify` affected repo после lockfile install; +targeted tests ускоряют итерацию, но не подменяют required gate. Wire changes +дополнительно требуют backend contract check, HTTP/DB changes — relevant isolated +suites по local Testing. Форматирующие `lint:fix`/`format` — mutations, не proof. diff --git a/.ai/conventions/testing.md b/.ai/conventions/testing.md new file mode 100644 index 0000000..9e9e6c5 --- /dev/null +++ b/.ai/conventions/testing.md @@ -0,0 +1,27 @@ +# Test Intent + +Перед нетривиальным тестом сформулируй наблюдаемый результат и риск регрессии +в описании задачи/PR, рядом с тестом либо в рабочем отчёте. Отдельный artifact +не обязателен. Для trivial low-risk изменения отдельный Test Intent не нужен. + +Краткая форма, только применимые пункты: + +- **Поведение:** что увидит пользователь/caller и какую ошибку должен поймать тест. +- **Варианты и границы:** значимые альтернативы, edge/error states, concurrency. +- **Исключено:** какие детали реализации не являются контрактом. +- **Уровень:** самая дешёвая boundary, которая надёжно различает правильный + и сломанный исход; критерии — в local testing convention. + +Пример: «Два refresh с одним token: один успешный ответ, второй 401; +новая session остаётся рабочей. Проверить на реальной изолированной SQLite. +Не проверять порядок private calls». Mock race не доказывает DB atomicity. + +Для каждого теста ответь: «Какой regression он обнаружит?» Не создавай +`Foo.test.tsx` из-за появления `Foo.tsx`. По умолчанию не тестируй obvious JSX, +CSS classes, каждый wrapper/aria attribute, decorators, trivial CRUD wiring, +framework и сторонние библиотеки. Проверяй доступность через пользовательское +действие/смысл, когда это риск; декларативный атрибут сам по себе не test intent. + +Bug fix сначала воспроизводит исход на responsible boundary. После подходящих +проверок остановись: количество тестов и coverage ради coverage не являются целью. +Новые harness/framework/fixtures не нужны, когда существующий setup покрывает риск. diff --git a/.ai/migrations/conventions-v1/audit.md b/.ai/migrations/conventions-v1/audit.md new file mode 100644 index 0000000..c46c71f --- /dev/null +++ b/.ai/migrations/conventions-v1/audit.md @@ -0,0 +1,178 @@ +# Conventions v1: semantic audit + +Дата: 2026-09-15. Read-only audit production code; conventions изменены отдельно. +Snapshot: root `2567a755569e6ef6ba12063b62f202fe52884ce7`, frontend +`0244acf429c525b23117f165bbe52ef5403eefd3`, backend +`660b250ecd05e38784690b9a9adb381764c0ca7c`. На старте worktrees чистые. + +## Scope и метод + +Прочитаны existing AGENTS/.ai, manifests, TypeScript/Biome/ESLint, dependency +rules, test/Storybook setup и OpenAPI flow. Проверены representative paths: +auth/refresh, bank cards/operations/summary, portfolio/income page VM, account +errors, instrument search и T-Bank/MOEX/NCR boundaries. `rg` использован для +callers, HTTP/Prisma/domain imports, raw fetch/SQL, logging, error strings и +state patterns. Точечная executable reproduction проверяет ValidationPipe → filter. + +Это reviewed inventory подтверждённых findings, не полный автоматический +счётчик всех файлов. P0 — явное архитектурное нарушение / высокий риск; +P1 — неверная ответственность / существенная inconsistency; +P2 — cleanup / naming / менее важная consistency issue. +Confidence: High / Medium / Low. P0 по проверенной области не выявлены; +это не security certification. + +| Severity | Findings | +| --- | --- | +| P0 | 0 | +| P1 | 4 | +| P2 | 3 | + +## BE-001 — Validation теряет ошибки полей + +- **Location:** [configureApp](../../../backend/src/main.ts), строка 28; + [HttpExceptionFilter](../../../backend/src/common/filters/http-exception.filter.ts), + `catch` / `validationErrors`. +- **Convention:** [Errors](../../../backend/.ai/conventions/backend-error-handling.md), + [требование](../../../specs/openspec/specs/user-authentication/spec.md#requirement-стандартные-ответы-об-ошибках). +- **Current state:** стандартный ValidationPipe формирует `message: string[]`; + filter трактует его как `ValidationError[]`, возвращая пустой `errors`. +- **Expected state:** invalid request → 400 / VALIDATION_ERROR с применимыми + `{ pointer, detail }`; вложенные поля обрабатываются согласно контракту. +- **Severity:** P1. **Confidence:** High. +- **Reason:** публичный контракт теряет field details; типовой filter unit test + и OpenAPI schema parity не проходят реальную validation pipeline. +- **Suggested migration:** отдельный behavior fix в Auth/API validation slice: + согласовать output pipe с filter; доказать через configured HTTP request. + Не добавлять новый validation framework. + +## BE-002 — Repository решает business eligibility и HTTP outcome + +- **Location:** [BankCardsRepository](../../../backend/src/modules/banking/bank-cards/infrastructure/bank-cards.repository.ts), + `card(active)` и `write`; callers в + [BankOperationsService](../../../backend/src/modules/banking/bank-cards/application/bank-operations.service.ts). +- **Convention:** [Data](../../../backend/.ai/conventions/backend-data.md), + [application boundaries](../../../backend/.ai/conventions/backend-architecture.md#ответственность-и-направление-зависимостей). +- **Current state:** repository импортирует application `conflict/missing`, + решает запрет archived card и переводит общий P2002 в DuplicateCategory. +- **Expected state:** repository владеет owner-filtered reads, transaction/retry + и техническим DB outcome; application/domain определяет eligibility/public error. +- **Severity:** P1. **Confidence:** High. +- **Reason:** storage helper знает business policy нескольких callers; + P2002 не всегда означает duplicate category. Утечка доступа не утверждается. +- **Suggested migration:** Cards/Operations slice: перенести policy и mapping + к ответственным сценариям, сохранив lookup/check/write внутри той же transaction, + retry budget, idempotency, CAS и действующие public codes. + +## FE-001 — Самостоятельные банковские действия находятся в entity UI + +- **Location:** [BankCardForm](../../../frontend/src/entities/bank-card/ui/BankCardForm.tsx), + [CategoryManager](../../../frontend/src/entities/bank-card/ui/CategoryManager.tsx); + consumers — CardsPage и CardPage. +- **Convention:** [FSD ownership](../../../frontend/.ai/conventions/frontend-architecture.md#выбор-владельца). +- **Current state:** entity UI координирует создание/редактирование карты, + idempotency intent, submit, version adoption, создание/редактирование категорий. +- **Expected state:** самостоятельные пользовательские workflows имеют feature + owner; entity сохраняет модели, queries и presentation понятия карты/категории. +- **Severity:** P1. **Confidence:** High. +- **Reason:** это полноценные reused capabilities с собственным lifecycle, + а не вывод о слое из наличия одной кнопки или mutation. +- **Suggested migration:** по одному действию в Cards slice; presentation fields + сохранять/reuse, не перемещать всю entity и все её hooks механически. + +## FE-002 — Views банковских страниц владеют query state selection + +- **Location:** [CardPage.view](../../../frontend/src/pages/card/ui/CardPage.view.tsx), + [CardsPage.view](../../../frontend/src/pages/cards/ui/CardsPage.view.tsx), + соответствующие `CardPage.vm.ts` / `CardsPage.vm.ts` рядом. +- **Convention:** [Container/VM/view](../../../frontend/.ai/conventions/frontend-ui.md#container-vm-и-view). +- **Current state:** VM отдаёт query objects; views выбирают initial/cached/error + states через `data/isLoading/isError/isPlaceholderData` и вызывают `refetch`. +- **Expected state:** VM выбирает presentation states и retry callbacks, + views отображают данные/действия. DOM scroll/focus остаются UI responsibility. +- **Severity:** P1. **Confidence:** High. +- **Reason:** presentation зависит от query lifecycle и принимает решения, + которые existing account-income pattern уже размещает в VM. +- **Suggested migration:** Cards slice, сначала один экран: перенести state + selection без изменения loading/cached/recovery, URL pagination и scroll anchor. + Не строить generic state machine для всех страниц. + +## FE-003 — Application navigation находится в shared UI + +- **Location:** [AppShell](../../../frontend/src/shared/ui/AppShell.tsx), + `items`, `useLocation`, `Outlet`, nav rendering; public export в shared/ui. +- **Convention:** [FSD](../../../frontend/.ai/conventions/frontend-architecture.md), + [mental model](../../../frontend/.ai/architecture.md). +- **Current state:** shared owner содержит конкретные `/accounts`, `/cards`, + `/stocks`, `/bonds`, `/settings` и application navigation. +- **Expected state:** navigation/composition принадлежит app; shared сохраняет + только действительно общие presentation primitives при текущей потребности. +- **Severity:** P2. **Confidence:** High. +- **Reason:** известное legacy placement уже отмечалось старым convention; + dependency graph не доказывает отсутствие business meaning в shared. +- **Suggested migration:** отдельный navigation slice вместе с app callers и CSS, + без новой оболочки над Mantine и без переразметки пользовательского UI. + +## FE-004 — Connection recovery классифицируется повторно + +- **Location:** [AccountPageError](../../../frontend/src/pages/account/ui/components/AccountPageError.view.tsx), + [AccountsPageError](../../../frontend/src/pages/accounts/ui/components/AccountsPageError.view.tsx), + [StocksPage.view](../../../frontend/src/pages/stocks/ui/StocksPage.view.tsx), + [BondsPage.view](../../../frontend/src/pages/bonds/ui/BondsPage.view.tsx). +- **Convention:** [Frontend errors](../../../frontend/.ai/conventions/frontend-data.md#errors), + [pattern convergence](../../conventions/code-health.md#search--reuse--converge). +- **Current state:** несколько owners повторяют `TBANK_NOT_CONNECTED` / + `INVALID_TBANK_TOKEN` comparisons; presentation/retry actions различаются. +- **Expected state:** общая классификация принадлежит одному допустимому owner, + локальный текст/actions остаются у сценария; unknown code сохраняет fallback. +- **Severity:** P2. **Confidence:** High. +- **Reason:** повторяется техническое decision rule. Различный UI сам по себе + не нарушение; generated `code: string` не разрешает выдумывать закрытый enum. +- **Suggested migration:** Accounts/Portfolio, затем Instruments/Settings: + унифицировать только classification с сохранением всех текущих recovery paths. + +## BE-003 — Invalid-token protocol основан на Error.message + +- **Location:** [TbankClient](../../../backend/src/integrations/tbank/tbank.client.ts), + `call` (throw Error); [TbankTokenService](../../../backend/src/integrations/tbank/tbank-token.service.ts), + `translateInvalidToken`; [InstrumentMetadataService](../../../backend/src/integrations/tbank/instrument-metadata.service.ts), + invalid-token catch. +- **Convention:** [Errors](../../../backend/.ai/conventions/backend-error-handling.md), + [Integrations](../../../backend/.ai/conventions/backend-integrations.md). +- **Current state:** low-level client бросает `Error('INVALID_TBANK_TOKEN')`, + два consumers сравнивают строку сообщения для выбора recovery/HTTP mapping. +- **Expected state:** один явный local typed/code outcome на integration boundary, + public HTTP translation остаётся у существующего owner. +- **Severity:** P2. **Confidence:** High. +- **Reason:** изменение диагностического текста ломает machine behavior; + raw provider exception сейчас уже скрыт, подтверждённой утечки нет. +- **Suggested migration:** Instruments/Settings integration slice; заменить + marker и всех consumers одной связной правкой, сохранив auth/unavailable + distinction и wire codes. Общий enterprise error framework не нужен. + +## Что не признано нарушением + +- Page-local components account/income/events и их flat views допустимы. + Размер файла, `map` или handler сами по себе не определяют feature/entity. +- Прямые Prisma queries связных auth/banking/connection services допускаются + existing data convention; standalone use cases уже используют repository. +- URL-applied values и editable draft dates — разные состояния; синхронизация + draft после navigation не равна лишнему server-state cache. +- Повторы `filter(Boolean).join(" · ")` формируют display metadata, а не CSS + classes; это не основание устанавливать `clsx`. +- `ProblemDetailsDto.code: string` и typed wire literals — ограничение принятого + генератора, не основание создавать ручную копию backend enum. +- Stories показывают synthetic presentation; отсутствие story/test у каждого + component не считается finding. По выполненным поискам не найдены raw fetch + вне shared/api, production console или raw SQL; это не доказательство полноты. + +## Evidence BE-001 + +Из backend после `nvm use --silent`: одноразовый `node -r ts-node/register` +загрузил actual CredentialsDto, ValidationPipe и HttpExceptionFilter. +Input `{ email: 'bad', password: 'x' }` → exception `message: string[]` → +`{ status: 400, code: 'VALIDATION_ERROR', errors: [] }` (assert PASS). +DB/порты/внешняя сеть не использовались. Это proof пары pipe/filter; +исправление всё равно требует configured HTTP regression test. + +Immutable baseline этого анализа; статусы закрытия и evidence будущих работ +вести в [plan](plan.md), не переписывая snapshot под новый код. diff --git a/.ai/migrations/conventions-v1/plan.md b/.ai/migrations/conventions-v1/plan.md new file mode 100644 index 0000000..6a3aa1c --- /dev/null +++ b/.ai/migrations/conventions-v1/plan.md @@ -0,0 +1,75 @@ +# Conventions v1: migration plan + +Исходный [audit](audit.md): 7 открытых findings — P0: 0, P1: 4, P2: 3. +Это число reviewed findings, а не все violations репозитория. Production +refactoring в задаче conventions не выполнялся. Порядок ниже — рекомендуемые +отдельные работы, без обязательного запуска SDD или repository-wide cleanup. + +## Vertical slices + +| Slice / порядок | Findings и target ownership | Сохранить / Test Intent | Migration и verification | +| --- | --- | --- | --- | +| 1. Auth: invalid request | BE-001; configureApp validation + HTTP filter | 400/code, field pointers/details, safe unknown errors, whitelist semantics; invalid request не вызывает use case | Отдельный behavior fix и configured HTTP regression, без структурного рефакторинга. Auth e2e, filter tests, contract check, backend verify | +| 2. Cards: создание/редактирование | FE-001; feature workflows + entity presentation/queries | Submit blocking, ввод после failure, idempotency ID, version conflict/adoption, archived editing | Сначала одно действие, затем следующее. Только structural change. Existing banking/form RTL/MSW, relevant stories, deps/knip и frontend verify | +| 3. Cards/Operations: просмотр и запись | FE-002 + BE-002; page VM states; application policy; repository transaction | Cached snapshot/refetch failure, URL period/page, scroll anchor; user isolation, CAS, atomic transfer/refund, balances | Раздельные frontend/backend commits; не менять behavior вместе с переносом. RTL/MSW и browser pagination; banking unit + SQLite/HTTP, relevant durability, verify обоих repos | +| 4. Accounts/Portfolio: recovery | FE-004, первая группа consumers; общий classifier у допустимого owner | NOT_FOUND → accounts, connection → settings, transient error → retry; сохранить различия текста/actions и fallback unknown code | Structural convergence одного decision rule. Account/portfolio RTL/MSW и frontend verify; оставшиеся instrument consumers учитываются до полного закрытия FE-004 | +| 5. Instruments/Settings: connection | FE-004 оставшиеся consumers + BE-003; integration marker/translation | Invalid token и unavailable различаются; bounded retry, cache isolation/generation, metadata fallback | Сначала marker и все backend callers, отдельно frontend classifier reuse. T-Bank/token/metadata tests, relevant API e2e, frontend recovery tests, verify affected repos | +| 6. Navigation между областями | FE-003; app composition | Active links desktop/mobile, nested Outlet, breadcrumbs, auth fallback, keyboard и narrow viewport | Перенос existing AppShell с CSS/callers, без нового wrapper/UI redesign. Route tests, deps/knip, browser proof и frontend verify | + +Каждый slice начинает с классификации responsibility и просмотра всех callers. +До edits зафиксируй target owner, сохраняемое behavior и применимый +[Test Intent](../../conventions/testing.md). Structural refactor и behavior +change идут отдельными шагами/commits; завершение требует semantic review, +relevant checks и обновления статуса finding с evidence. + +## Ratchet + +Все семь IDs пока **Open**. Следующая migration фиксирует здесь: +`ID → Open/Closed → repo commit/PR → команда и результат`. +Закрывай finding только после миграции всех перечисленных consumers; частичный +перенос не уменьшает count. Новую находку добавляй отдельным ID с severity, +confidence и scope; изменение охвата не выдавай за улучшение качества. + +Цель — последовательно уменьшать open baseline и не добавлять новых нарушений +в diff. Existing exceptions остаются видимыми и не становятся образцами нового +кода. Исходный audit сохраняет facts своего snapshot. + +## Deterministic follow-ups + +Полный mapping — в [guardrails](../../conventions/guardrails.md). +Existing dependency-cruiser поддерживает `--ignore-known` (подтверждено local +CLI help). При отдельной работе над same-layer imports сначала проверить +candidate rule; если существующих violations нет, baseline не нужен. +При legacy использовать native exact baseline, без directory-wide exclusions. + +Backend import boundaries и `no-console` при повторном regression можно +закрепить встроенными ESLint rules. Markdown `.ai` можно включить явными путями +в existing markdownlint workflow, когда согласована доступность соседних +checkout; сегодня проверка `.ai` выполнена локально тем же инструментом. +Новые analyzers, scripts, dependencies или custom baseline infrastructure +в этом изменении не создаются. + +## Verification conventions v1 + +Проверено 2026-09-15 на согласованном workspace из трёх веток +`codex/engineering-conventions`; docs/specs остались без изменений. + +| Проверка | Результат | +| --- | --- | +| Root `python3 test-workspace-tracking.py` | PASS: exact `.ai` allowlist, соседние repos и вымышленные local/private files сохранены | +| Из docs: `npm exec -- markdownlint-cli2 --config .markdownlint-cli2.jsonc '../AGENTS.md' '../README.md' '../.ai/**/*.md' '../frontend/AGENTS.md' '../frontend/.ai/**/*.md' '../backend/AGENTS.md' '../backend/.ai/**/*.md'` | PASS: 31 Markdown, включая новые файлы, 0 ошибок style/relative links/anchors | +| Docs `npm run verify` | PASS: 42 Markdown, 0 ошибок; проверены также входящие межрепозиторные ссылки | +| Frontend `npm run deps`, `npm run deps:storybook` | PASS: 297/298 modules, 0 нарушений настроенных dependency rules | +| Backend ValidationPipe → filter reproduction | Подтверждён BE-001: invalid CredentialsDto → 400/VALIDATION_ERROR, `errors: []` | +| `git diff --check` в root/frontend/backend/docs/specs | PASS | + +Semantic review: маршруты UI-only, API/contract, auth, tests-only, helper/package, +Prisma, logging и migration достигают нужных owners; ссылки compatibility +не дублируют таблицы routing. Общие policy имеют root owner, stack-specific +исключения остались локальными. Проверены новые файлы и отсутствие production, +manifest/lockfile, generated code, CI/release и docs/specs изменений. + +Application build/coverage/e2e целиком не запускались: изменение Markdown и +root tracking не меняет production behavior. Existing dependency checks и +воспроизведение BE-001 подтверждают только указанные границы, не отсутствие +всех semantic/security проблем. Full gates выполняются при будущих code slices. diff --git a/.gitignore b/.gitignore index 49d18c8..de0d4e3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,21 @@ !/AGENTS.md !/README.md !/test-workspace-tracking.py +!/.ai/ +/.ai/* +!/.ai/conventions/ +/.ai/conventions/* +!/.ai/conventions/git.md +!/.ai/conventions/dependencies.md +!/.ai/conventions/code-health.md +!/.ai/conventions/testing.md +!/.ai/conventions/guardrails.md +!/.ai/migrations/ +/.ai/migrations/* +!/.ai/migrations/conventions-v1/ +/.ai/migrations/conventions-v1/* +!/.ai/migrations/conventions-v1/audit.md +!/.ai/migrations/conventions-v1/plan.md !/.nvmrc !/package.json !/package-lock.json diff --git a/AGENTS.md b/AGENTS.md index e214f77..5b51b01 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,29 +1,31 @@ -# WealthLedger +# WealthLedger workspace -## Workspace +`frontend`, `backend`, `docs`, `specs` и root — независимые Git-репозитории. +Root tracking ограничен allowlist; локальные данные и nested repos в него +не добавляются. `references/**`, dated audits и архивы — read-only. +Структура/запуск — в [README](README.md). -`frontend`, `backend`, `docs` и `specs` — независимые Git-репозитории. -Root — отдельный Git-репозиторий `WealthLedger/main`; команды Git выполняй -из нужного repo. Root tracking ограничен allowlist в `.gitignore`; -вложенные repos и локальные данные в main не добавляй, включая `git add -f`. -Структура checkout и запуск — в [README](README.md). -`references/**`, dated audits и архивы — read-only источники. +Начни с результата задачи и affected areas. Прочитай local AGENTS затронутого +repo; проследи поведение/инварианты и найди owner с 1–3 примерами до реализации. +Код показывает механизм, а случайный аналог не переопределяет контракт. +Работай напрямую; OpenSpec/SDD lifecycle нужен только по явному запросу. -## Работа - -1. Начинай с задачи пользователя, ожидаемого результата и affected areas. Работай напрямую: OpenSpec/SDD artifacts, approvals и archive не являются prerequisite. Используй прежний SDD workflow только по явному запросу. -2. Прочитай local `AGENTS.md` affected repo. Для frontend/backend прочитай его `.ai/CONVENTIONS.md` и `.ai/SOURCES.md`, затем только темы, которые активируют задача, пути или обнаруженная граница. -3. До выбора реализации проследи поведение и инварианты, найди ближайший существующий owner и 1–3 подходящих примера. Код показывает реализацию; документированный контракт не выводится из случайного аналога. -4. Выбирай простейшее полное решение у ответственного владельца. Новая abstraction или dependency требует текущей потребности; посторонний рефакторинг оставляй отдельно. -5. Выбирай тип знания по задаче через [вход docs](docs/README.md): новая функция или межрепозиторное поведение → соответствующая область, спецификации, нужные ADR, затем реализация; ошибка с неясным ожиданием → спецификация, docs, код и тесты; архитектурное изменение → архитектура/ADR, затронутые спецификации и код. Локальная ошибка сборки, линтера или очевидный implementation bug → инструкции и код нужного repo; документация → docs/AGENTS.md и CONTRIBUTING.md. Расширяй контекст по конкретному пробелу; missing required source или конфликт требований сообщи до зависимого решения. -6. При новой границе и перед завершением повтори routing по actual staged, unstaged и новым файлам. Сохраняй чужие изменения; уточнение контекста не расширяет задачу. -7. Проверь результат по задаче, рискованным случаям и local gates. Сохрани краткие команды/результаты; сообщи оставшиеся ограничения. Успех lint/tests не заменяет semantic review. +| Задача / граница | Прочитать | +| --- | --- | +| Frontend / backend | [Frontend](frontend/AGENTS.md) / [Backend](backend/AGENTS.md), затем только активированные темы | +| Docs / specs | [Docs AGENTS](docs/AGENTS.md) / [Specs AGENTS](specs/AGENTS.md) | +| Новая функция, неясное ожидание, межрепозиторное поведение | [Docs entry](docs/README.md) → нужная область/spec/ADR; локальный build/lint bug начинает с repo instructions/code | +| Новый helper/hook/wrapper/pattern, refactor, выбор scope | [Code health / change scope](.ai/conventions/code-health.md) | +| Dependency или tooling | [Dependencies](.ai/conventions/dependencies.md), [guardrails](.ai/conventions/guardrails.md) | +| Нетривиальные tests | [Test Intent](.ai/conventions/testing.md) и local Testing | +| Git, ветка, commit, PR, tracking | [Git](.ai/conventions/git.md) | +| Release/hotfix, main↔develop, release config/CI refs | [Release process](README.md#релизы-и-ветки) | +| Migration существующих conventions findings | [Audit/plan](.ai/migrations/conventions-v1/plan.md) | Перед Node-командой активируй `.nvmrc` affected repo через `nvm use --silent`. - -## Ветки и релизы - -Новая работа — в отдельной ветке от `develop`, затем PR в `develop`. -Прямой push в `main` запрещён. Commits и заголовки PR — Conventional Commits. -Перед release/hotfix, merge между `main` и `develop`, изменением release-конфигурации -или CI refs прочитай [процесс релиза](README.md#релизы-и-ветки). +При новой границе и перед завершением повтори routing по staged/unstaged/new +files; сохраняй чужие изменения. Missing required source или конфликт требований +сообщи до зависимого решения. После code diff выполни +[Boy Scout review](.ai/conventions/code-health.md#boy-scout-review); проверь результат +по рискам и [relevant gates](.ai/conventions/guardrails.md#выбор-финального-gate). +Сохрани краткие команды/результаты и ограничения; успех lint не заменяет semantic review. diff --git a/README.md b/README.md index 3237b47..bea7cda 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,8 @@ git clone ssh://git@git.personal.ksv741.ru:2222/WealthLedger/specs.git specs ### Что хранится в main -Root отслеживает входные документы, npm/release-конфигурацию, changelog, +Root отслеживает входные документы, общие engineering conventions и их migration +inventory в `.ai/`, npm/release-конфигурацию, changelog, проверки tracking/release и три CI workflow. Точный allowlist — в `.gitignore`. Остальные root-пути игнорируются по умолчанию: вложенные repos, `.env`, базы и WAL, импорты выписок, PDF, `references/`, diff --git a/test-workspace-tracking.py b/test-workspace-tracking.py index 7f18bc2..a15a797 100644 --- a/test-workspace-tracking.py +++ b/test-workspace-tracking.py @@ -16,6 +16,13 @@ def check(ignore_rules): git("init", "--quiet") allowed = { + '.ai/conventions/git.md', + '.ai/conventions/dependencies.md', + '.ai/conventions/code-health.md', + '.ai/conventions/testing.md', + '.ai/conventions/guardrails.md', + '.ai/migrations/conventions-v1/audit.md', + '.ai/migrations/conventions-v1/plan.md', '.gitea/workflows/commits.yml', '.gitea/workflows/security.yml', '.gitea/workflows/verify.yml', @@ -49,6 +56,9 @@ def check(ignore_rules): ".agents/local.md", "unknown-directory/data.txt", "unknown-file.txt", "scripts/private.txt", ".gitea/private.txt", ".gitea/workflows/private.yml", "node_modules/private.txt", + ".ai/private.md", ".ai/conventions/private.md", + ".ai/migrations/private.md", ".ai/migrations/conventions-v1/private.md", + ".ai/migrations/other/private.md", ): target = root / name target.parent.mkdir(parents=True, exist_ok=True)