Files
docs/audits/2026-08-frontend-repository-audits/frontend-repository-audit-2026-08-28.md
T

152 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.