152 lines
19 KiB
Markdown
152 lines
19 KiB
Markdown
# Repository Audit — frontend
|
||
|
||
Дата аудита: 2026-08-28. Аудит выполнен только для чтения; этот файл — единственная запись, созданная в рамках работы. Эталонный каталог использовался как ориентир, а не как источник достоверной информации.
|
||
|
||
## Scope and Confidence
|
||
|
||
**Целевой репозиторий:** /Users/ksv741/Project/vibe/WealthLedger/frontend
|
||
**Эталон:** /Users/ksv741/Project/vibe/WealthLedger/references/frontend-ai-template
|
||
|
||
Цель — React 19 / TypeScript / Vite SPA WealthLedger с typed OpenAPI-клиентом, TanStack Query и FSD-направлением зависимостей. Проверены AGENTS.md, README, локальные docs, package и lock-конфигурация, hooks, Biome, dependency-cruiser, Knip, Vitest/Vite/TypeScript-конфигурация, основной app/auth/API путь, entity query hooks, финансовые страницы, настройки T-Bank connection и их тесты. В эталоне проверены эквивалентные scripts, hook, OpenAPI guardrail, тестовая/документационная структура и representative source tree.
|
||
|
||
Исходное и финальное состояние target: ветка main без tracked или untracked изменений; git diff и staged diff пусты. Эталон — read-only каталог без собственного Git-репозитория, поэтому его рабочее состояние не верифицировалось.
|
||
|
||
Запущены только заведомо read-only проверки:
|
||
|
||
- npm run lint — прошло; Biome проверил 128 файлов, без исправлений.
|
||
- npm run knip — прошло.
|
||
- npm run deps — прошло; dependency-cruiser проверил 136 модулей и 484 зависимости, нарушений нет.
|
||
|
||
В audit-terminal команда nvm use --silent недоступна, поэтому Node 22 через .nvmrc не был активирован этой оболочкой; version mismatch не диагностировался. Не запускались npm ci, verify, build, coverage-тесты, typecheck и генерация OpenAPI: они могут создавать или обновлять install, coverage, dist, tsbuildinfo либо generated artifacts. Реальный backend, browser E2E и runtime OpenAPI contract также не запускались. Эти ограничения снижают уверенность только в runtime-свойствах, не в трёх статических findings ниже.
|
||
|
||
### Сравнение с эталоном
|
||
|
||
| Эквивалентная обязанность | Вердикт | Причина |
|
||
|---|---|---|
|
||
| Контроль синхронности OpenAPI source и generated types | **adopt from reference** | В эталоне есть небольшой temp-file check, включённый в verify и pre-commit; target хранит такие же два артефакта, но не имеет проверки. |
|
||
| Coverage anti-regression | **target is already better** | Target задаёт явные coverage thresholds в Vitest; в эталонной конфигурации их нет. |
|
||
| Storybook, Playwright, browser MSW и mock development mode | **reference approach is unnecessary** | Эталон — демонстрационный SPA-шаблон. Target AGENTS.md прямо запрещает эти добавления без approved change, а проверенный scope не показал конкретной проблемы, которую оправдывает весь этот lifecycle. |
|
||
|
||
## Executive Summary
|
||
|
||
Репозиторий находится в хорошем состоянии по типизации, зависимостям, локальному quality gate и навигации для агента. Biome, Knip и dependency-cruiser проходят, а auth, query retry, error states и display-logic имеют целевые тесты.
|
||
|
||
Два высокоприоритетных пробела делают эту базу менее надёжной, чем обещают её инварианты:
|
||
|
||
1. Смена или удаление T-Bank connection оставляет connection-scoped финансовые данные в React Query cache.
|
||
2. Committed OpenAPI snapshot и generated types могут разойтись, а verify всё равно остаться зелёным.
|
||
|
||
Отдельно, обязательная VM/view и state-view конвенция уже расходится с несколькими production screens. Это создаёт агентам и разработчикам два конкурирующих образца вместо одного канонического.
|
||
|
||
## Priority Findings
|
||
|
||
| Severity | Finding | Category | Action | Effort |
|
||
|---|---|---|---|---|
|
||
| high | F1 — Нет единого invalidation boundary для данных T-Bank connection | Architecture / Data correctness | change | medium |
|
||
| high | F2 — Generated OpenAPI type artifact не проверяется на синхронность | Tooling / Contract safety | add | small |
|
||
| medium | F3 — Обязательная VM/view конвенция расходится с production code | Architecture / AI Readiness | change | medium |
|
||
|
||
## Findings
|
||
|
||
### F1 — Нет единого invalidation boundary для данных T-Bank connection
|
||
|
||
**Category:** Architecture / Data correctness
|
||
**Severity:** high
|
||
**Confidence:** high
|
||
**Action:** change
|
||
**Effort:** medium
|
||
|
||
**Observed:** Успешные PUT и DELETE T-Bank connection обновляют status и инвалидируют только query списка brokerage accounts. Portfolio, operations, future events, income summary и income analytics продолжают жить в cache по accountId.
|
||
|
||
**Evidence:** src/features/tbank-connection/ui/TbankConnectionSection.tsx:60-79 вызывает invalidateQueries только с brokerageAccountsQueryKey. Все перечисленные data hooks создают самостоятельные account-scoped query keys и оставляют данные видимыми: src/entities/portfolio/api/usePortfolio.ts:12-21, src/entities/broker-operation/api/useBrokerageOperations.ts:35-49, src/entities/brokerage-event/api/useBrokerageEvents.ts:9-19, src/entities/income/api/useIncome.ts:9-41. UI pattern намеренно оставляет Client Cached Snapshot на экране во время background refresh; для замены либо удаления source connection это старое состояние уже не является допустимым snapshot. Тесты settings-сценария проверяют только invalidation account-list: src/features/tbank-connection/ui/TbankConnectionSection.test.tsx:52-98.
|
||
|
||
**Counter-evidence:** Logout и неуспешный refresh очищают весь QueryClient в src/features/auth/model/auth.tsx:57-62, а account-list действительно инвалидируется при смене connection. Эта защита не срабатывает, когда пользователь остаётся в той же authenticated session и только заменяет или удаляет connection.
|
||
|
||
**Why it matters:** После замены token или отключения connection пользователь может увидеть cache старого portfolio, ledger, events либо income до следующего рефетча. Для финансовых данных это ошибочная принадлежность данных к уже изменившемуся upstream connection; на активном экране она особенно заметна, потому что UI специально отображает cached result при обновлении.
|
||
|
||
**Reference repository:** Нет эквивалентного authenticated external-connection domain в todo-template; сравнение неприменимо.
|
||
|
||
**Recommended direction:** Ввести один явно названный ownership boundary для всех данных, производных от T-Bank connection, и при replace/delete очищать либо корректно инвалидировать весь этот scope, а не только список счетов. Предпочтение следует отдать удалению snapshot, который больше нельзя показывать. Добавить один интеграционный тест, seed-ящий representative query каждого класса и подтверждающий, что после обоих mutation paths эти данные недоступны для рендера.
|
||
|
||
### F2 — Generated OpenAPI type artifact не проверяется на синхронность
|
||
|
||
**Category:** Tooling / Contract safety
|
||
**Severity:** high
|
||
**Confidence:** high
|
||
**Action:** add
|
||
**Effort:** small
|
||
|
||
**Observed:** Target хранит committed OpenAPI snapshot и committed generated schema.d.ts, но package scripts и pre-commit не имеют команды, которая пересоздаёт schema во временном месте и сравнивает результат. Поэтому source snapshot можно изменить без regeneration, а TypeScript продолжит проверять устаревший generated contract.
|
||
|
||
**Evidence:** package.json:9-25 содержит openapi:generate, но не openapi:check; verify на строке 20 его не вызывает. .husky/pre-commit:1-2 запускает только lint-staged и deps. При этом AGENTS.md:42 утверждает, что openapi:check есть в verify и pre-commit, а docs/CONVENTIONS.md:12-14 называет OpenAPI check обязательным pre-commit step. Поиск по target не нашёл openapi:check. docs/API-LAYER.md:1-5 подтверждает, что snapshot и generated types должны быть committed и обновляться отдельно.
|
||
|
||
**Counter-evidence:** Документация предписывает ручной порядок «скопировать snapshot → сгенерировать types», а backend располагает отдельной runtime contract-проверкой. Ни один из этих механизмов не делает локальный frontend gate самодостаточным: ошибка ручной regeneration остаётся незамеченной до более поздней стадии.
|
||
|
||
**Why it matters:** Зелёный verify может закрепить stale request/response types после изменения backend contract. Это подрывает заявленную compile-time безопасность API и способно привести к неверно сформированному запросу либо к коду, опирающемуся на устаревшую response shape.
|
||
|
||
**Reference repository:** **adopt from reference.** references/frontend-ai-template/scripts/check-openapi.mjs:1-32 генерирует во временный файл и байтово сравнивает его с committed schema; package.json:13-28 и .husky/pre-commit:1 включают проверку в verify и hook. Это небольшой fitting guardrail, не требующий нового dependency или runtime backend.
|
||
|
||
**Recommended direction:** Добавить target-specific offline check для openapi.json → schema.d.ts, подключить его к verify и pre-commit, затем исправить AGENTS.md/README/conventions так, чтобы они описывали фактический gate. Не включать backend fetch или автоматическую генерацию в verify: gate должен оставаться детерминированным и read-only относительно tracked artifacts.
|
||
|
||
### F3 — Обязательная VM/view конвенция расходится с production code
|
||
|
||
**Category:** Architecture / AI Readiness
|
||
**Severity:** medium
|
||
**Confidence:** high
|
||
**Action:** change
|
||
**Effort:** medium
|
||
|
||
**Observed:** Документация требует, чтобы data-driven finance page была container → VM → view, а top-level states/sections находились в отдельных files. Несколько текущих screens демонстрируют обратное: AccountEventsPage одновременно читает route params, вызывает query hook и объявляет Loading/Error/Empty/Loaded components в одном file; AccountPage и AccountsPage держат четыре top-level state components в одном .view.tsx; AccountIncomePage держит три.
|
||
|
||
**Evidence:** docs/CONVENTIONS.md:12-14 и AGENTS.md:36 описывают правило как обязательное; docs/UI-PATTERN.md описывает владение data/state VM и отдельные state views. Прямой контрпример — src/pages/account-events/ui/AccountEventsPage.tsx:1-116. Остальные representative deviations: src/pages/account/ui/AccountPage.view.tsx:54-147, src/pages/accounts/ui/AccountsPage.view.tsx:42-124 и src/pages/account-income/ui/AccountIncomePage.view.tsx:29-52. Для contrast src/pages/account-operations/ui уже использует VM и отдельные component state views. Lint и dependency check проходят, поскольку правило не имеет machine enforcement.
|
||
|
||
**Counter-evidence:** FSD direction, public APIs и circular imports успешно проверяются dependency-cruiser; большинство finance data derivation действительно вынесено в VM. Это ограничивает scope finding до понятности и consistency page-level pattern, а не до общей архитектурной целостности.
|
||
|
||
**Why it matters:** Новый агент получает обязательную инструкцию, которую production tree нарушает в нескольких наиболее заметных screens. Это увеличивает navigation cost и делает review субъективным: один и тот же UI change можно «правильно» продолжить по двум несовместимым образцам.
|
||
|
||
**Reference repository:** **both are acceptable в части принципа**, но более мелкая per-state организация эталона подтверждает полезность разделения там, где data/state branches уже самостоятельны. Переносить его Storybook/e2e scaffolding или создавать новый page framework не требуется.
|
||
|
||
**Recommended direction:** Выбрать один честный canonical pattern: привести существующие named pages к заявленной VM/state-view границе там, где branches самостоятельны, либо сузить документацию, прямо разрешив компактные colocated views для небольших screens. После выбора оставить FSD проверки как есть; новый dependency или тяжёлый linter ради одного file-layout правила не оправдан.
|
||
|
||
## Missing Practices
|
||
|
||
- **F1:** отсутствует connection-scoped cache ownership и test, проверяющий его очистку при trust-boundary change.
|
||
- **F2:** отсутствует детерминированная offline-проверка committed OpenAPI source и generated type artifact.
|
||
|
||
## Over-engineering / Simplification Opportunities
|
||
|
||
Материальной production over-engineering проблемы не найдено. Компоненты LoadingSkeleton и StateView повторно используют доступные state regions; query/auth seams обслуживают реальные trust и concurrency requirements. Самая выгодная simplification — не добавлять параллельные способы описания page architecture: закрыть F3 одним каноническим соглашением.
|
||
|
||
## AI Readiness
|
||
|
||
Оценка: **хорошая, с одним существенным navigation risk**. AGENTS.md кратко направляет к специализированной документации, scripts узнаваемы, FSD-boundary gate и generated-code exclusion ясны. F2 делает API change небезопасным несмотря на эти подсказки, а F3 заставляет агент выбирать между текстовыми правилами и существующим кодом. Устранение этих двух findings улучшит predictability без расширения toolchain.
|
||
|
||
## What Is Already Good
|
||
|
||
- Строгая TypeScript-конфигурация, exact dependency versions, package-lock, Biome, Husky/commitlint и Node 22 intent хорошо определяют baseline.
|
||
- Dependency-cruiser проверяет FSD direction, отсутствие circular imports и public slice API; текущий graph проходит (136 modules, 484 dependencies).
|
||
- API transport отделён в shared typed client, auth request исключены из Bearer attachment, refresh имеет single-flight путь и tests на retry/failure.
|
||
- Query hooks задают явные staleTime и retry policy; UI/tests покрывают cached snapshot, refresh error, route boundary, portfolio display и operation filtering.
|
||
- Target coverage thresholds — более практичный anti-regression guard, чем эталонный template без порогов.
|
||
|
||
## Recommended Order
|
||
|
||
### Do now
|
||
|
||
1. Закрыть F1: сделать T-Bank connection change полноценной границей очистки data cache и добавить regression test.
|
||
2. Закрыть F2: добавить offline OpenAPI synchronization check в verify/pre-commit, затем синхронизировать документацию.
|
||
|
||
### Do next
|
||
|
||
1. Закрыть F3: согласовать page-level VM/state-view rule с production tree и обновить только выбранный source of truth.
|
||
|
||
### Consider later
|
||
|
||
Переоценить browser-level critical journeys только при approved change, который меняет соответствующий пользовательский риск; текущая статическая evidence не оправдывает широкий test-stack перенос из template.
|
||
|
||
### Do not add
|
||
|
||
- Storybook, Playwright, browser MSW worker или mock development mode только потому, что они есть в эталоне.
|
||
- CI/CD, deployment tooling или cloud configuration: system scope остаётся локальным.
|
||
- Ещё один FSD/framework linter для VM/view file layout: сначала нужно устранить противоречие F3, а текущий dependency-cruiser уже покрывает доказанные dependency risks.
|