docs: establish workspace engineering conventions
Workspace security / security (push) Successful in 7s
Workspace verify / verify (push) Successful in 20s
Workspace security / security (pull_request) Successful in 6s
Workspace verify / verify (pull_request) Successful in 20s
Conventional commits / commits (pull_request) Successful in 10s
Workspace security / security (push) Successful in 7s
Workspace verify / verify (push) Successful in 20s
Workspace security / security (pull_request) Successful in 6s
Workspace verify / verify (pull_request) Successful in 20s
Conventional commits / commits (pull_request) Successful in 10s
This commit is contained in:
@@ -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 под новый код.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user