19 KiB
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 имеют целевые тесты.
Два высокоприоритетных пробела делают эту базу менее надёжной, чем обещают её инварианты:
- Смена или удаление T-Bank connection оставляет connection-scoped финансовые данные в React Query cache.
- 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
- Закрыть F1: сделать T-Bank connection change полноценной границей очистки data cache и добавить regression test.
- Закрыть F2: добавить offline OpenAPI synchronization check в verify/pre-commit, затем синхронизировать документацию.
Do next
- Закрыть 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.