docs: document frontend audit remediation delivery
This commit is contained in:
@@ -0,0 +1,510 @@
|
||||
# Repository Audit: WealthLedger Frontend
|
||||
|
||||
**Дата:** 2026-08-31
|
||||
**Session ID:** `01a05590-9243-7df0-9664-508acc5fc3e9`
|
||||
**Invoked skills:** `repository-audit`
|
||||
**Supporting skills invoked:** none
|
||||
**External research performed:** no
|
||||
|
||||
## Scope and Confidence
|
||||
|
||||
**Target:** `frontend/`, Git branch `main`.
|
||||
**Target commit:** `7ab043dce63a3fbacbfc9ac7d15b00dd714a541f`
|
||||
(`docs: remove useless`).
|
||||
**Initial target state:** `git status --porcelain=v1 --untracked-files=all` был
|
||||
пуст; целевой репозиторий не содержал tracked или untracked изменений.
|
||||
|
||||
**Reference:** `references/frontend-ai-template/`. Директория не является
|
||||
Git-репозиторием, поэтому commit identity недоступна. Идентичность
|
||||
зафиксирована SHA-256 checksum аудируемого содержимого:
|
||||
`7fced9c6906bdc846847eec40e05aac7c2c59f9fe593acc298eaab4bb29e2a9a`.
|
||||
Checksum получен как SHA-256 отсортированного списка per-file SHA-256 и
|
||||
относительных путей. Из scope исключены `node_modules` на любой глубине,
|
||||
`.idea`, `.playwright-mcp`, cache-, build- и coverage-директории. Эталон
|
||||
использовался только для сравнения эквивалентных обязанностей, не как authority.
|
||||
|
||||
Аудит standalone и read-only: OpenSpec/SDD workflow не запускался. Единственная
|
||||
запись — этот отчёт вне `frontend/` и reference. В inventory вошли все 145
|
||||
tracked-файлов target (117 под `src`, 22 test-файла) и 220 релевантных файлов
|
||||
reference после исключения локальных зависимостей и ephemeral artifacts.
|
||||
Углублённо проверены manifests/lockfile, TypeScript/Vite/Vitest/Biome/Knip/
|
||||
dependency-cruiser/Husky configuration, repository instructions и frontend
|
||||
docs, auth/session и API seams, routing/error boundary, representative finance
|
||||
pages, entities, ViewModels, tests и эквивалентные reference responsibilities.
|
||||
По всему `src` выполнены searches импортов, network calls, generated types,
|
||||
tests и крупных modules; не каждый CSS-файл и не весь generated OpenAPI файл
|
||||
читались построчно.
|
||||
|
||||
Выполненные проверки:
|
||||
|
||||
- `npm run deps` — exit 0; 136 modules и 484 dependency edges, нарушений нет;
|
||||
- `npm run knip` — exit 0, findings отсутствуют;
|
||||
- offline generation `openapi-typescript` во временный файл — текущий
|
||||
`schema.d.ts` байтово совпадает со snapshot;
|
||||
- controlled invalid-date probe под Node 22 —
|
||||
`RangeError: Invalid time value`, подтверждающий F-03.
|
||||
|
||||
Полный `npm run verify`, `test:coverage`, build, Storybook и E2E не запускались:
|
||||
они создают или могут изменять `coverage`, `dist`, reports и caches, что не
|
||||
соответствует read-only контракту аудита. Также не выполнялись install,
|
||||
dependency advisory scan, backend runtime contract test и browser-based visual/
|
||||
accessibility checks. Поэтому выводы о текущем полном gate, supply-chain
|
||||
уязвимостях, реальном backend и визуальном поведении не заявляются.
|
||||
|
||||
**Общая confidence:** high для статической архитектуры, tooling и найденных
|
||||
расхождений; medium для production/release risk из-за локальной границы системы
|
||||
и отсутствия runtime/browser evidence.
|
||||
|
||||
**Preservation check:** после всех inspection/validation commands target
|
||||
остался на том же commit с пустым porcelain status; повторный reference checksum
|
||||
совпал с исходным. Временный generated artifact удалён. Единственное
|
||||
сохранённое изменение сессии — этот Markdown report.
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Репозиторий в целом находится в хорошем состоянии: небольшой React/Vite client
|
||||
имеет строгий TypeScript, один typed HTTP boundary, risk-oriented Vitest/MSW
|
||||
tests, coverage floors, dead-code analysis и более сильные dependency rules,
|
||||
чем reference. Критических findings нет.
|
||||
|
||||
Наибольшую ценность дадут F-01 и F-02: восстановить детерминированную проверку
|
||||
generated OpenAPI artifact и закрыть реальный login journey существующим test
|
||||
stack. F-03 — небольшой, но уже воспроизводимый trust-boundary defect в URL date
|
||||
validation. F-04 уточняет существенное ограничение заявленного public-API gate:
|
||||
он не видит type-only dependencies и уже пропускает семь внутренних imports.
|
||||
Остальная архитектурная стоимость сосредоточена в двух ownership seams (F-05,
|
||||
F-08) и в документации, которая местами описывает удалённые либо избыточно
|
||||
жёсткие guardrails (F-06, F-07).
|
||||
|
||||
Эталон полезен как подтверждение ценности OpenAPI drift check, но его Storybook,
|
||||
browser MSW, Playwright harness, локальный OpenSpec и 17 agent skills не должны
|
||||
переноситься в target: текущая система локальна, а эти поверхности не решают
|
||||
наблюдаемую проблему пропорционально своей стоимости.
|
||||
|
||||
## Priority Findings
|
||||
|
||||
| Severity | Finding | Category | Action | Effort |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| high | F-01. Generated OpenAPI types не защищены от drift | Tooling / API Contract | add | small |
|
||||
| high | F-02. Основной auth journey не проверяется | Testing / Security | add | small |
|
||||
| medium | F-03. Некорректная календарная дата бросает исключение | Correctness / Validation | change | small |
|
||||
| medium | F-04. Type-only dependencies обходят public-API guardrail | Architecture / Tooling | change | small |
|
||||
| medium | F-05. Ubiquitous Money formatting принадлежит Portfolio | Architecture / Ownership | change | medium |
|
||||
| medium | F-06. Entry-point docs обещают отсутствующие проверки | AI Readiness / Documentation | change | small |
|
||||
| low | F-07. File-splitting rule создаёт ложный hard invariant | Complexity / AI Readiness | simplify | small |
|
||||
| low | F-08. T-Bank status обходит общий authenticated-query seam | Architecture / Maintainability | change | small |
|
||||
|
||||
## Findings
|
||||
|
||||
### F-01. Generated OpenAPI types не защищены от drift
|
||||
|
||||
**Category:** Tooling / API Contract
|
||||
**Severity:** high
|
||||
**Confidence:** high
|
||||
**Action:** add
|
||||
**Effort:** small
|
||||
|
||||
**Observed:** Frontend хранит и OpenAPI snapshot, и generated
|
||||
`schema.d.ts`, но canonical `verify` не проверяет, что второй детерминированно
|
||||
получен из первого. Workflow полагается на ручное выполнение
|
||||
`npm run openapi:generate` после копирования snapshot.
|
||||
|
||||
**Evidence:** `frontend/package.json:17-20` содержит `openapi:generate`, но
|
||||
`verify` состоит только из typecheck, Biome, coverage, Knip,
|
||||
dependency-cruiser и build. `frontend/docs/API-LAYER.md:3-5` прямо описывает
|
||||
ручные два шага. Backend `test:openapi-contract` сравнивает runtime contract с
|
||||
frontend snapshot, но не snapshot с `frontend/src/shared/api/generated/schema.d.ts`.
|
||||
Текущий controlled check с генерацией в `/private/tmp` показал
|
||||
`OPENAPI_GENERATED_MATCH yes`, то есть finding относится к отсутствующему
|
||||
guardrail, а не к текущему рассинхрону. TypeScript и build являются
|
||||
counter-evidence только для внутренней согласованности кода со stale generated
|
||||
типами; происхождение этих типов они доказать не могут.
|
||||
|
||||
**Why it matters:** Изменение snapshot без генерации может одновременно пройти
|
||||
frontend `verify` и backend runtime-contract check, оставив приложение
|
||||
скомпилированным против старого публичного контракта. Для auth и финансовых
|
||||
response shapes это даёт silent contract risk в главном typed boundary.
|
||||
|
||||
**Reference repository:** **adopt from reference — capability only.** Reference
|
||||
имеет `scripts/check-openapi.mjs`, `openapi:check` и включает его в `verify` и
|
||||
pre-commit (`reference/package.json:13-28`,
|
||||
`reference/scripts/check-openapi.mjs:1-32`). Это узкий guardrail с прямой
|
||||
проблемой и без новой dependency. Остальной reference toolchain для него не
|
||||
нужен.
|
||||
|
||||
**Recommended direction:** Добавить один non-mutating offline check: с помощью
|
||||
уже установленного `openapi-typescript` генерировать temp artifact, сравнивать
|
||||
его с committed `schema.d.ts` и включить сравнение в canonical `verify`.
|
||||
Не синхронизировать backend и не переписывать tracked files внутри gate.
|
||||
|
||||
### F-02. Основной auth journey не проверяется
|
||||
|
||||
**Category:** Testing / Security
|
||||
**Severity:** high
|
||||
**Confidence:** high
|
||||
**Action:** add
|
||||
**Effort:** small
|
||||
|
||||
**Observed:** Test suite хорошо покрывает восстановление session, Bearer retry,
|
||||
очистку cache, anonymous redirects и одну отрицательную registration validation,
|
||||
но ни один тест не выполняет успешный login/register/logout через public UI/
|
||||
provider boundary. В частности, untested остаётся связка `AuthForm` →
|
||||
`AuthProvider.authenticate` → auth endpoint → in-memory access token → переход
|
||||
на protected route.
|
||||
|
||||
**Evidence:** Поиск по всем 22 test-файлам нашёл auth assertions только в
|
||||
`src/shared/api/client.test.ts`, `src/shared/api/authenticated-query.test.tsx`,
|
||||
`src/features/auth/model/auth.test.tsx`, `src/features/auth/ui/AuthForm.test.tsx`
|
||||
и `src/app/routes/AppRoutes.test.tsx`. `auth.test.tsx:11-102` проверяет три
|
||||
refresh/401 сценария; `AuthForm.test.tsx:16-41` — только несовпадающее
|
||||
подтверждение пароля; route test восстанавливает session через refresh.
|
||||
Tests не вызывают `login`, `register` или `logout`, не отправляют валидную
|
||||
форму и не проверяют `LoginForm`/`RegisterForm` navigation. Это проверено
|
||||
repository-wide search, а не выводом из отсутствия отдельного filename.
|
||||
|
||||
**Why it matters:** Auth — security-sensitive и critical user journey по
|
||||
действующей risk-based testing policy. Независимо покрытые низкоуровневые seams
|
||||
не обнаружат поломку их композиции: неверный endpoint/body, потерянное session
|
||||
state update, неработающий `returnTo`, navigation до завершения auth или
|
||||
неочищенный UI после logout.
|
||||
|
||||
**Reference repository:** **reference approach is unnecessary.** Reference
|
||||
имеет Playwright и browser MSW, но его E2E покрывает Todo demo, не auth. Перенос
|
||||
всего browser harness ради этого риска добавит больше lifecycle cost, чем
|
||||
существующий Vitest + Testing Library + MSW boundary.
|
||||
|
||||
**Recommended direction:** Добавить минимальный risk-focused integration test
|
||||
на существующем stack, который вводит валидные credentials, проходит реальный
|
||||
`AuthProvider`/MSW login и наблюдает protected landing/session outcome. Logout
|
||||
или failure следует добавлять только там, где их риск не покрыт уже имеющимися
|
||||
provider/client tests; Playwright для этого finding не требуется.
|
||||
|
||||
### F-03. Некорректная календарная дата бросает исключение
|
||||
|
||||
**Category:** Correctness / Validation
|
||||
**Severity:** medium
|
||||
**Confidence:** high
|
||||
**Action:** change
|
||||
**Effort:** small
|
||||
|
||||
**Observed:** `isDate` сначала проверяет только форму `YYYY-MM-DD`, затем без
|
||||
проверки validity вызывает `toISOString()`. Строка вроде `2026-99-99` проходит
|
||||
regex и приводит к `RangeError`, а не к validation result `false`.
|
||||
|
||||
**Evidence:** `src/pages/account-income/ui/AccountIncomePage.vm.ts:35-46` подаёт
|
||||
`isDate` в Zod refinements; `:51-71` читает даты из untrusted URL search params;
|
||||
`:114-132` использует `new Date(...).toISOString()` в `isDate` и затем снова в
|
||||
`validPeriod`. Controlled Node 22 probe над этой же операцией вернул
|
||||
`INVALID_DATE_THROW RangeError: Invalid time value`. Existing income tests
|
||||
покрывают analytics, empty bucket, chart и recoverable server error, но не
|
||||
malformed URL/form dates. Общий `RouteErrorBoundary` ограничивает blast radius,
|
||||
но превращает validation error в аварийный route state.
|
||||
|
||||
**Why it matters:** Пользователь или внешняя ссылка может сломать income route
|
||||
одним syntactically-shaped query parameter. Это доступность и корректность на
|
||||
trust boundary; exception bypasses предусмотренное восстановление default
|
||||
period и field-level validation.
|
||||
|
||||
**Reference repository:** **neither reflects a directly comparable case.**
|
||||
Reference валидирует numeric route params через Zod `safeParse`, но не содержит
|
||||
эквивалентной календарной логики, поэтому переносить его hook не нужно.
|
||||
|
||||
**Recommended direction:** Сделать date predicate total: проверить parsed date
|
||||
до `toISOString` либо валидировать год/месяц/день безопасным стандартным
|
||||
преобразованием, затем зафиксировать malformed query/form date регрессионным
|
||||
тестом. Новая date library не нужна.
|
||||
|
||||
### F-04. Type-only dependencies обходят public-API guardrail
|
||||
|
||||
**Category:** Architecture / Tooling / AI Readiness
|
||||
**Severity:** medium
|
||||
**Confidence:** high
|
||||
**Action:** change
|
||||
**Effort:** small
|
||||
|
||||
**Observed:** Dependency-cruiser проверяет cycles, layer direction и public
|
||||
entrypoints только для dependencies, существующих после TypeScript compilation.
|
||||
Type-only edges не входят в graph, хотя repository docs формулируют public-API
|
||||
правило без такого исключения. В результате семь consumers импортируют
|
||||
внутренний generated module `@/shared/api/generated/schema` напрямую.
|
||||
|
||||
**Evidence:** В `frontend/dependency-cruiser.cjs:45-48` задан `tsConfig`, но нет
|
||||
`tsPreCompilationDeps`; установленный dependency-cruiser 18.2.0 устанавливает
|
||||
его default в `false` (`node_modules/dependency-cruiser/src/main/options/defaults.mjs:9`).
|
||||
`frontend/docs/CONVENTIONS.md:9` и `frontend/docs/STRUCTURE.md:11-13` требуют
|
||||
cross-slice imports через `index.ts`, а `frontend/AGENTS.md:34` называет это
|
||||
dependency-cruiser-enforced boundary. При этом прямые type-only imports
|
||||
`@/shared/api/generated/schema` находятся в пяти entity API hooks,
|
||||
`features/auth/model/auth.tsx` и
|
||||
`features/tbank-connection/ui/TbankConnectionSection.tsx`;
|
||||
`frontend/src/shared/api/index.ts` не реэкспортирует `paths`. Успешный
|
||||
`npm run deps` не является counter-evidence: эти edges отсутствуют в
|
||||
анализируемом graph. Counter-evidence по масштабу риска — остальные найденные
|
||||
type-only imports идут через public APIs в разрешённом направлении, а текущие
|
||||
семь generated imports не создают runtime coupling.
|
||||
|
||||
**Why it matters:** Заявленный machine-enforced invariant фактически не
|
||||
распространяется на compile-time architecture. Новый type-only upward import,
|
||||
type cycle или обращение к внутренностям другого slice сможет пройти canonical
|
||||
gate; существующий generated seam уже демонстрирует расхождение code и policy.
|
||||
Severity остаётся medium, потому что текущие edges стираются при compilation и
|
||||
не создают runtime cycle или bundle dependency.
|
||||
|
||||
**Reference repository:** **adopt from reference — capability, not exception.**
|
||||
Reference включает `tsPreCompilationDeps: true`, но исключает generated subtree
|
||||
из dependency analysis. Target может сохранить более строгий и ясный public API,
|
||||
не копируя это исключение.
|
||||
|
||||
**Recommended direction:** Реэкспортировать generated `paths` как type из
|
||||
`shared/api/index.ts`, перевести семь consumers на `@/shared/api` и включить
|
||||
`tsPreCompilationDeps: true` в существующем dependency-cruiser config. Если
|
||||
generated path действительно должен быть публичным исключением, сначала
|
||||
зафиксировать его явно и всё равно проверять остальные type-only edges. Новая
|
||||
dependency или второй architecture checker не нужны.
|
||||
|
||||
### F-05. Ubiquitous Money formatting принадлежит Portfolio
|
||||
|
||||
**Category:** Architecture / Ownership
|
||||
**Severity:** medium
|
||||
**Confidence:** high
|
||||
**Action:** change
|
||||
**Effort:** medium
|
||||
|
||||
**Observed:** Общий display formatter `formatMoney` типизирован через
|
||||
`Portfolio["totalsByCurrency"][number]` и экспортируется из Portfolio entity,
|
||||
хотя используется для Brokerage Account, Ledger Entry, Income Analytics и
|
||||
Future Cash Event. `formatSnapshotTime` и instrument logo helper также частично
|
||||
разделяются через тот же Portfolio public API.
|
||||
|
||||
**Evidence:** Canonical domain glossary определяет Money как самостоятельный
|
||||
ubiquitous value `{ currency, value }`, который frontend форматирует для
|
||||
отображения (`docs/architecture/domain-glossary.md:20-28`). Реализация в
|
||||
`src/entities/portfolio/model/display.ts:1-18` выводит Money type из Portfolio.
|
||||
Repository-wide usage показывает импорт Portfolio formatter из
|
||||
`src/entities/brokerage-account/ui/BrokerageAccountList.tsx`,
|
||||
`src/entities/broker-operation/ui/LedgerEntryRow.tsx`, account/events/income
|
||||
pages и их section views. `npm run deps` проходит, потому что текущие rules
|
||||
контролируют direction/public entrypoints, но не смыслового владельца peer
|
||||
entities. Counter-evidence: formatter один, протестирован и не дублируется;
|
||||
проблема именно в owner/type dependency, а не в реализации форматирования.
|
||||
|
||||
**Why it matters:** Изменение Portfolio transport shape или реорганизация
|
||||
Portfolio заставляет менять несвязанные account, ledger и income consumers.
|
||||
Граница утверждает, что общий Money concern принадлежит одной product entity,
|
||||
и легализует entity-to-entity coupling, которое dependency tool не различает.
|
||||
|
||||
**Reference repository:** **reference approach is unnecessary.** Todo-only
|
||||
reference не моделирует shared Money value и не предлагает применимого owner.
|
||||
|
||||
**Recommended direction:** Переместить только действительно ubiquitous
|
||||
display primitives (в первую очередь Money formatting и, при подтверждении
|
||||
общего смысла, date formatting) в нейтральный существующий lower-level owner.
|
||||
Оставить `formatShare` и portfolio error behavior в Portfolio. Не вводить Money
|
||||
service, interface hierarchy или новую domain layer ради одного formatter.
|
||||
|
||||
### F-06. Entry-point docs обещают отсутствующие проверки
|
||||
|
||||
**Category:** AI Readiness / Documentation
|
||||
**Severity:** medium
|
||||
**Confidence:** high
|
||||
**Action:** change
|
||||
**Effort:** small
|
||||
|
||||
**Observed:** Человеческий entrypoint сообщает, что `verify` отклоняет неверный
|
||||
Node runtime и проверяет repository hygiene, а conventions утверждают, что
|
||||
pre-commit запускает OpenAPI check. Ни одной из этих команд в текущих scripts/
|
||||
hook нет.
|
||||
|
||||
**Evidence:** `frontend/README.md:25-27` перечисляет runtime и hygiene;
|
||||
`frontend/docs/CONVENTIONS.md:14` перечисляет pre-commit OpenAPI check.
|
||||
Фактически `frontend/package.json:9-22` не содержит `runtime:check`,
|
||||
`hygiene:check` или `openapi:check`, а `.husky/pre-commit` выполняет только
|
||||
`npx lint-staged` и `npm run deps`. Counter-evidence: `.nvmrc`,
|
||||
`engines.node = 22.x` и repository-local `AGENTS.md` корректно требуют
|
||||
`nvm use --silent`; canonical config остаётся discoverable. Расхождение
|
||||
ограничено двумя stale prose claims, а не всей документацией.
|
||||
|
||||
**Why it matters:** Новый разработчик или coding agent получает ложное
|
||||
completion evidence и может считать runtime, env hygiene или generated artifact
|
||||
проверенными. Особенно опасно сочетание с F-01: docs называют guardrail, которого
|
||||
больше нет.
|
||||
|
||||
**Reference repository:** **both are acceptable only where config and prose
|
||||
agree.** Reference действительно включает свой `openapi:check`, но его намного
|
||||
больший documentation surface не нужен target. Target уже лучше как concise,
|
||||
canonical entrypoint; ему требуется синхронизация нескольких утверждений, а не
|
||||
перенос reference docs.
|
||||
|
||||
**Recommended direction:** Привести README и CONVENTIONS к фактическим scripts:
|
||||
описать manual Node activation и реальные hook/gate stages. Если F-01 добавлен,
|
||||
упомянуть только реально включённый OpenAPI check. Не возвращать отдельные
|
||||
runtime/hygiene scripts без нового конкретного failure mode.
|
||||
|
||||
### F-07. File-splitting rule создаёт ложный hard invariant
|
||||
|
||||
**Category:** Complexity / AI Readiness
|
||||
**Severity:** low
|
||||
**Confidence:** high
|
||||
**Action:** simplify
|
||||
**Effort:** small
|
||||
|
||||
**Observed:** UI docs объявляют обязательным отдельный file для каждого
|
||||
top-level state и каждого именуемого page section, но несколько текущих modules
|
||||
сознательно хранят cohesive presentation рядом. Самый заметный пример —
|
||||
388-line `AccountIncomePage.view.tsx` с loading/error/loaded и шестью semantic
|
||||
section components.
|
||||
|
||||
**Evidence:** `frontend/docs/CONVENTIONS.md:12-13` и
|
||||
`frontend/docs/UI-PATTERN.md:30-35` формулируют правило как Must/«нельзя».
|
||||
`src/pages/account-income/ui/AccountIncomePage.view.tsx:29-380` содержит
|
||||
`AccountIncomePageLoading`, `AccountIncomePageError`, loaded view, summary,
|
||||
comparisons, trend, breakdown и forecast. Подобная colocated state structure
|
||||
есть в `AccountsPage.view.tsx` и небольшом T-Bank view. Counter-evidence:
|
||||
важная граница container → VM → props-only view в проверенных finance pages
|
||||
соблюдается; большой income view читается последовательно и покрыт одним
|
||||
behavior-level page test. Поэтому само количество components/files не является
|
||||
failure mode.
|
||||
|
||||
**Why it matters:** Непроверяемое и уже нарушенное Must заставляет agents либо
|
||||
игнорировать authority, либо создавать file/ barrel sprawl без улучшения
|
||||
ownership. Оно также маскирует действительно важный invariant — views не должны
|
||||
владеть data fetching и collection derivation.
|
||||
|
||||
**Reference repository:** **reference approach is unnecessary.** Reference
|
||||
часто создаёт отдельную директорию и file для каждого Todo state; это полезно
|
||||
для component catalog demo, но увеличивает navigation cost для текущего product
|
||||
client и не должно становиться механической нормой.
|
||||
|
||||
**Recommended direction:** Оставить VM/view ownership rule обязательным, а
|
||||
file splitting сделать locality-based guidance: выделять section, когда у неё
|
||||
есть самостоятельная responsibility, reuse, test/change cadence или она мешает
|
||||
пониманию owner. Не добавлять line-count checker и не требовать one-component-
|
||||
per-file.
|
||||
|
||||
### F-08. T-Bank status обходит общий authenticated-query seam
|
||||
|
||||
**Category:** Architecture / Maintainability
|
||||
**Severity:** low
|
||||
**Confidence:** high
|
||||
**Action:** change
|
||||
**Effort:** small
|
||||
|
||||
**Observed:** Все entity queries используют shared `useAuthenticatedQuery`,
|
||||
который подписывается на session snapshot. T-Bank connection status вместо
|
||||
этого импортирует `useAuth` из соседней feature и вручную задаёт
|
||||
`enabled: Boolean(accessToken)` обычному `useQuery`.
|
||||
|
||||
**Evidence:** `src/shared/api/authenticated-query.ts:13-33` владеет session
|
||||
gating; его используют brokerage account, operation, event, income и portfolio
|
||||
hooks. `src/features/tbank-connection/ui/TbankConnectionSection.vm.ts:1-15`
|
||||
создаёт второй механизм и feature → feature dependency на `features/auth`.
|
||||
`dependency-cruiser.cjs` не запрещает peer-feature dependencies, поэтому
|
||||
успешный `npm run deps` не является counterexample. Counter-evidence: текущее
|
||||
поведение функционально совпадает, access token обновляется через AuthProvider,
|
||||
а mutations всё равно идут через общий authenticated fetch.
|
||||
|
||||
**Why it matters:** Изменение определения authenticated readiness,
|
||||
`configureAuthClient` или session clearing может примениться ко всем entity
|
||||
queries, но не к T-Bank status. Соседняя feature становится зависимой от auth
|
||||
implementation detail при уже существующей lower-level seam.
|
||||
|
||||
**Reference repository:** **not comparable.** Reference не имеет auth/session
|
||||
boundary.
|
||||
|
||||
**Recommended direction:** Переиспользовать existing authenticated-query seam
|
||||
для status query и убрать feature-to-feature dependency. Новая abstraction или
|
||||
расширение dependency rules ради единственного случая не нужны.
|
||||
|
||||
## Missing Practices
|
||||
|
||||
- F-01 — отсутствует доказательство происхождения committed generated API
|
||||
types из committed snapshot.
|
||||
- F-02 — отсутствует risk-focused integration proof главного auth journey.
|
||||
- F-03 — trust-boundary date validation не total и не имеет malformed-input
|
||||
regression coverage.
|
||||
- F-04 — type-only dependencies не входят в заявленный public-API/layer gate;
|
||||
уже существуют семь direct generated-internal imports.
|
||||
|
||||
Отсутствие CI/CD и deployment automation не считается finding: canonical
|
||||
`docs/architecture/system-overview.md` явно ограничивает текущую поставку
|
||||
локальной системой, а `frontend/AGENTS.md` требует отдельного approved change
|
||||
для CI, Playwright или Storybook.
|
||||
|
||||
## Over-engineering / Simplification Opportunities
|
||||
|
||||
- F-07 — сузить file-splitting Must до правила ownership/locality, сохранив
|
||||
важную VM/view границу.
|
||||
- Не переносить из reference Storybook, browser MSW mode, Playwright harness,
|
||||
локальный `openspec/` и 17 repository-provided skills: для target нет
|
||||
наблюдаемой проблемы, оправдывающей этот context и maintenance surface.
|
||||
- F-05 следует исправлять переносом маленьких shared primitives, не созданием
|
||||
Money service/interface/factory.
|
||||
- Не добавлять второй FSD framework или checker: текущий dependency-cruiser уже
|
||||
проверяет cycles, public entrypoints и layer direction лучше reference.
|
||||
|
||||
## AI Readiness
|
||||
|
||||
AI readiness выше средней: `AGENTS.md` короткий, различает hard guardrails и
|
||||
review guidance, направляет к специализированным docs и canonical multi-repo
|
||||
authority; scripts и public APIs предсказуемы; generated seam явно помечена;
|
||||
dependency-cruiser и Knip снижают необходимость загружать длинные инструкции.
|
||||
Главные ухудшения — F-04 (type-only blind spot), F-06 (несовпадение prose с
|
||||
executable truth) и F-07 (непроверяемый structural Must). Reference agent
|
||||
surface значительно больше и для target был бы регрессом context locality.
|
||||
|
||||
## What Is Already Good
|
||||
|
||||
- Строгая и воспроизводимая основа: Node 22 contract, npm lockfile, exact direct
|
||||
dependency versions, strict TypeScript и Biome.
|
||||
- `npm run deps` подтверждает отсутствие cycles/layer/public-API violations в
|
||||
анализируемых runtime edges; target имеет отдельные cycle/public-API rules,
|
||||
которых нет в reference, но F-04 ограничивает полноту этого доказательства.
|
||||
- Один HTTP owner: repository-wide search не нашёл прямой network access вне
|
||||
`src/shared/api/client.ts`; transport types выводятся из OpenAPI.
|
||||
- Auth design соответствует system/domain authority: access token остаётся в
|
||||
memory, refresh использует cookie и single-flight behavior; Bearer exclusion,
|
||||
refresh retry и cache clearing имеют focused tests.
|
||||
- 22 colocated test-файла покрывают loading/error/empty/cached snapshot,
|
||||
finance display, filtering/pagination, route boundary и T-Bank mutations.
|
||||
- Coverage floors, Knip, dependency checks и production build собраны в одном
|
||||
canonical local gate; controlled Knip/dependency runs успешны.
|
||||
- UI resilience и доступные loading/error regions не только описаны, но и
|
||||
представлены в code/tests; общий route error boundary ограничивает failures.
|
||||
- Текущий OpenAPI snapshot и generated types фактически синхронны; F-01 —
|
||||
prevention gap, не текущая поломка.
|
||||
|
||||
## Recommended Order
|
||||
|
||||
### Do now
|
||||
|
||||
1. F-01 — добавить non-mutating OpenAPI generated-artifact equivalence в
|
||||
canonical gate.
|
||||
2. F-02 — закрыть успешный auth journey существующим Vitest/MSW stack.
|
||||
3. F-03 — сделать date validation total и закрепить malformed-input case.
|
||||
4. F-04 — включить type-only edges и провести generated types через public API.
|
||||
5. F-06 — синхронизировать README/CONVENTIONS с фактическими scripts и hooks.
|
||||
|
||||
### Do next
|
||||
|
||||
1. F-05 — вернуть ubiquitous Money display primitive нейтральному owner без
|
||||
нового service layer.
|
||||
|
||||
### Consider later
|
||||
|
||||
1. F-08 — устранить единичный bypass authenticated-query seam при ближайшем
|
||||
изменении T-Bank connection.
|
||||
2. F-07 — упростить file-splitting guidance до решения по locality/ownership.
|
||||
|
||||
### Do not add
|
||||
|
||||
- Storybook, browser MSW mode или Playwright только потому, что они есть в
|
||||
reference; F-02 дешевле и надёжнее закрывается текущим integration stack.
|
||||
- Локальный OpenSpec store или копии reference agent skills: canonical SDD
|
||||
authority уже находится вне target и entrypoint достаточно короткий.
|
||||
- CI/CD, deployment или release platform вне отдельного approved scope:
|
||||
as-built system явно локальна.
|
||||
- Runtime validation каждого OpenAPI response без конкретного наблюдаемого
|
||||
trust-boundary failure; это существенно расширит runtime и schema ownership.
|
||||
- Новый FSD framework, line-count gate, one-component-per-file rule, Money
|
||||
service/interface или dependency ради findings, которые решаются текущими
|
||||
tools и небольшими ownership corrections.
|
||||
Reference in New Issue
Block a user