# Аудит качества WealthLedger Дата: 2026-08-13 Обновление: 2026-08-14 — повторная проверка находок после доработок `align-frontend-foundation` и `align-frontend-ui-patterns`. Статус каждой находки помечен: `[done]` закрыто, `[partial]` частично, `[open]` не тронуто. Статус: снимок текущей рабочей копии; исправления и OpenSpec-артефакты не создавались. ## Цель и охват Проведён тщательный аудит `frontend`, `backend`, `specs`, `docs`, корневых файлов координации и `.opencode`. Папка `references/` исключена. Оценивались качество кода, архитектура, безопасность, обработка ошибок, целостность данных, производительность, accessibility, качество сценариев тестирования, документация, OpenSpec и артефакты для AI-агентов. Текущая рабочая копия является источником фактов. История Git использовалась только для контекста жизненного цикла OpenSpec. Проценты и пороги test coverage намеренно не оценивались. ## Методика и ограничения Аудит сопоставляет локальные правила из `AGENTS.md`, OpenSpec, feature docs, ADR и фактический код. Проверялись критичные цепочки: регистрация и сессии, transport API, подключение T-Bank, счета и портфель, а также путь «спецификация -> реализация -> документация -> инструкция агенту». Ограничения: - Не проводились нагрузочные тесты, анализ production-конфигурации, live-проверки T-Bank, TLS/proxy и multi-instance развёртывания. - `references/` не анализировалась. - В `specs` присутствуют staged additions/deletions артефактов двух изменений; это нестабильное состояние рабочей копии, а не подтверждённый стабильный релиз. - Команда `opsx` отсутствует в среде, поэтому обязательная lifecycle-проверка `opsx-verify` не была выполнена. ## Карта зрелости | Область | Оценка | Основание | | --- | --- | --- | | Код backend | Требует приоритетных исправлений | Сильные криптография и денежная арифметика, но есть риски ротации и отзыва сессий. | | Код frontend | Удовлетворительно | Типизированный API и UI-состояния устойчивы; A-11/A-12 закрыты, но остаётся production fallback (A-03). | | Тестовые сценарии | Требует усиления | Frontend покрыт (transport + UI resilience); backend concurrent E2E отсутствует. | | Документация | Требует исправлений | Feature docs полезны, но часть устарела; architecture/development пока заглушки. | | OpenSpec | Удовлетворительно с риском процесса | Основные спецификации качественные и валидны, но lifecycle и traceability между репозиториями недостаточны. | | Артефакты для AI-агентов | Удовлетворительно с риском процесса | Границы репозиториев заданы хорошо, но OpenSpec context и multi-repo verification не формализованы. | ## Приоритетный backlog | Приоритет | ID | Область | Владелец | Краткое действие | Статус | Зависимости | | --- | --- | --- | --- | --- | --- | --- | | High | A-01 | Безопасность сессий | backend | Сделать refresh rotation атомарной. | open | Prisma conditional update/transaction. | | High | A-02 | Отзыв сессий | backend | Проверять активность `sid` либо документировать ограниченную модель отзыва access token. | open | Решение о DB lookup/cache в guard. | | High | A-03 | Production transport | frontend | Убрать fallback на `localhost` для production; использовать явный origin или same-origin `/api`. | open | Контракт reverse proxy и env. | | High | A-04 | Документация T-Bank | docs | Исправить устаревшее ограничение про счета и портфели. | open | Нет. | | High | A-05 | OpenSpec lifecycle | specs | Атомарно завершить архивирование либо восстановить активные артефакты. | partial | Подтверждение намеренного lifecycle. | | High | A-06 | Операционная документация | docs | Добавить архитектурную карту и workflow разработки/верификации. | partial | Подтверждение команд и границ от frontend/backend. | | Medium | A-07 | Регистрация | backend | Преобразовать unique-constraint race в `EMAIL_ALREADY_REGISTERED`. | open | Prisma error type/code. | | Medium | A-08 | API errors | backend | Исправить сериализацию validation errors. | open | Нет. | | Medium | A-09 | Сессии и cache | backend | Ввести retention/index для sessions и bounded TTL/LRU cache для portfolio metadata. | open | Политика retention и лимиты. | | Medium | A-10 | Большие портфели | backend | Ограничить параллелизм получения metadata ниже лимита очереди клиента. | open | Целевой latency limit. | | Medium | A-11 | Auth transport | frontend | Синхронизировать восстановленный token с `accessTokenRef`. | done | Нет. | | Medium | A-12 | FSD | frontend | Перевести внешние импорты слайсов на public API. | done | Нет. | | Medium | A-13 | Тестовые сценарии | frontend/backend | Добавить конкурентные auth E2E и behavior-тесты T-Bank UI. | partial | A-01, A-07; React Query test wrapper. | | Medium | A-14 | ADR и OpenSpec context | docs/specs/root | Актуализировать ADR, заполнить OpenSpec context и multi-repo evidence. | open | Согласование общей политики. | ## Находки ### High #### A-01. Ротация refresh token не атомарна — `[open]` - Доказательство: `backend/src/auth/auth.service.ts:75-96,148-166` выполняет read/verify/update в отдельных действиях. - Обновление 2026-08-14: код не менялся; риск сохраняется. - Риск: два параллельных запроса с одним cookie могут оба пройти проверку и получить access token; итоговый hash зависит от последней записи. - Минимальное действие: обновлять с условием по `id`, `refreshTokenHash`, активности и сроку жизни; считать нулевое число обновлений отказом. - Владелец: backend. Зависимости: Prisma conditional update/transaction. #### A-02. Отзыв сессии не прекращает действие уже выданного access JWT — `[open]` - Доказательство: `backend/src/auth/jwt.strategy.ts:19-23`, `backend/src/auth/jwt-auth.guard.ts:4-5`, `backend/src/auth/auth.service.ts:89-94,114-117` проверяют подпись, срок и тип token, но не состояние `sid`. - Обновление 2026-08-14: backend не менялся; риск сохраняется. - Минимальное действие: проверять активный неистёкший `sid` в guard/strategy либо явно принять и документировать bounded-revocation модель. - Владелец: backend. Зависимости: решение о lookup/cache. #### A-03. Production fallback направляет bearer token на localhost пользователя — `[open]` - Доказательство: `frontend/src/shared/api/config.ts:1-2` использует `http://localhost:3000` при отсутствии build-time URL; `frontend/src/shared/api/client.ts:54-57` направляет туда bearer-authenticated requests. - Обновление 2026-08-14: fallback `?? "http://localhost:3000"` перенесён в `frontend/src/shared/api/client.ts:6`; риск сохраняется. - Минимальное действие: требовать явный production API origin либо использовать same-origin `/api`; завершать build/start при отсутствии допустимой настройки. - Владелец: frontend. Зависимости: env и reverse-proxy contract. #### A-04. Документация T-Bank противоречит реализованным функциям — `[open]` - Доказательство: `docs/features/tbank-connection.md:38-40` откладывает счета и портфели, но `docs/features/brokerage-accounts.md:1-55` и `docs/features/portfolio-view.md:1-59` описывают их как поставленные. - Обновление 2026-08-14: не исправлено. - Минимальное действие: оставить в limitations только реально отложенные возможности. - Владелец: docs. Зависимости: нет. #### A-05. Активные OpenSpec changes находятся в неполном переходном состоянии — `[partial]` - Доказательство: `git status` в `specs` показывает staged additions/deletions `design.md`, delta-spec и `tasks.md` для `add-brokerage-accounts` и `add-portfolio-view`; правила требуют полный набор в `specs/AGENTS.md:16-17`. - Обновление 2026-08-14: `align-frontend-foundation` и `align-frontend-ui-patterns` архивированы в `archive/` и закоммичены в HEAD, но в индексе остаются 16 staged-записей `AD` (включая новые изменения); `git status` не чист, требуется `git reset` для завершения lifecycle. - Минимальное действие: одним коммитом завершить archive/move либо восстановить активные артефакты. - Владелец: specs. Зависимости: подтверждение lifecycle. #### A-06. Архитектурная и development-документация фактически отсутствуют — `[partial]` - Доказательство: `docs/architecture/README.md:1-3` и `docs/development/README.md:1-3` содержат только заглушки, хотя их назначение закреплено в `docs/AGENTS.md:15-17`. - Обновление 2026-08-14: добавлена `docs/development/frontend-workflow.md` (Node 22, `npm run verify`, `openapi:sync`); `docs/architecture/README.md` остаётся заглушкой. - Минимальное действие: добавить актуальную component/data-flow карту и руководство по setup, generation, verification и выпуску изменений. - Владелец: docs. Зависимости: подтверждение команд и границ frontend/backend. ### Medium #### A-07. Конкурентная регистрация возвращает 500 вместо контрактного 409 — `[open]` - Доказательство: `backend/src/auth/auth.service.ts:29-42` делает preflight `findUnique`, не обрабатывая unique-constraint вокруг `create`. - Риск: один из параллельных запросов с одинаковым email завершится не `EMAIL_ALREADY_REGISTERED`. - Минимальное действие: перехватывать Prisma unique-constraint и возвращать `ConflictException`. - Владелец: backend. Зависимости: Prisma error type/code. #### A-08. Формат validation error не соответствует фактическому Nest payload — `[open]` - Доказательство: `backend/src/common/filters/http-exception.filter.ts:23-38,58-66` трактует `message: string[]` как `ValidationError[]`. - Риск: публичный `VALIDATION_ERROR` содержит пустой `errors`, клиент не получает указания на неверные поля. - Минимальное действие: задать `exceptionFactory` с `ValidationError[]` или корректно сериализовать строки Nest. - Владелец: backend. Зависимости: нет. #### A-09. Сессии и portfolio cache не имеют границ роста — `[open]` - Доказательство: `backend/src/auth/auth.service.ts:137-165`, `backend/prisma/schema.prisma:28-39` не очищают sessions и не содержат expiry index; `backend/src/tbank/portfolio.service.ts:54-55,67-98,229-273` держит неограниченные process-local maps. - Риск: рост SQLite-таблицы и памяти при длительной эксплуатации. - Минимальное действие: retention/cleanup сессий и index `expiresAt`; bounded TTL/LRU с периодической очисткой cache. - Владелец: backend. Зависимости: политика retention, лимиты cache. #### A-10. Большой портфель может терять metadata из-за burst concurrency — `[open]` - Доказательство: `backend/src/tbank/portfolio.service.ts:131-133,229-272` запускает все lookups одновременно, `backend/src/tbank/tbank.client.ts:208-209` отклоняет новые вызовы после 50 queued. - Риск: часть позиций получает fallback FIGI/`Unknown instrument`, хотя данные доступны upstream. - Минимальное действие: bounded concurrency ниже лимита очереди либо очередь без hard rejection. - Владелец: backend. Зависимости: допустимая latency для крупных портфелей. #### A-11. Восстановленный access token не синхронизируется с transport ref — `[done]` - Доказательство: `frontend/src/features/auth/model/auth.tsx:66-75` обновляет state, но не `accessTokenRef.current`; `:100-110` настраивает shared client только из ref. - Обновление 2026-08-14: исправлено. `auth.tsx` синхронизирует `accessTokenRef.current` при bootstrap/refresh/login/logout/clear (строки 54,74,87,96,108); transport — единственный владелец Bearer header, покрыт тестами в `client.test.ts`. - Минимальное действие: синхронно обновлять ref при восстановлении token; затем удалить дублирующиеся headers. - Владелец: frontend. Зависимости: нет. #### A-12. Нарушается FSD public API boundary — `[done]` - Доказательство: `frontend/src/app/routes/AppRoutes.tsx:2-9`, `src/pages/accounts/ui/AccountsPage.tsx:5-10`, `src/pages/settings/ui/SettingsPage.tsx:4-6`, `src/features/tbank-connection/ui/TbankConnectionSection.tsx:13-16` импортируют внутренности слайсов вопреки `frontend/docs/STRUCTURE.md:11`. - Обновление 2026-08-14: исправлено. Импорты идут через public `index.ts`, добавлен `dependency-cruiser`; `npm run deps` проходит без нарушений (84 modules). - Минимальное действие: экспортировать нужные элементы через `index.ts` и импортировать только public API. - Владелец: frontend. Зависимости: нет. #### A-13. Критичные сценарии не защищены тестами — `[partial]` - Доказательство: `backend/test/app.e2e-spec.ts:36-85` не тестирует параллельные refresh/registration; `frontend/src/features/tbank-connection/ui/TbankConnectionSection.tsx:27-169` не имеет focused tests. - Обновление 2026-08-14: frontend-часть закрыта — transport-тесты (`client.test.ts`), `RouteErrorBoundary.test.tsx`, `AccountPage.test.tsx`, `TbankConnectionSection.view.test.tsx`; `vitest run` 30/30. Backend-часть (concurrent auth E2E) не добавлена — backend не менялся. - Минимальное действие: добавить concurrent E2E для auth и behavior tests для replace/delete/error/pending/invalidation UI. - Владелец: backend и frontend. Зависимости: A-01, A-07; React Query test wrapper. #### A-14. Контекст и verification evidence не адаптированы к multi-repo SDD — `[open]` - Доказательство: `specs/openspec/config.yaml:1-20` оставлен примером; `.opencode/skills/openspec-verify-change/SKILL.md:70-115` не требует evidence matrix по `frontend`, `backend`, `docs`; архивные tasks, например `specs/openspec/changes/archive/2026-08-13-add-portfolio-view/tasks.md:13-15,23-32`, не содержат commit SHA и результаты проверок. - Обновление 2026-08-14: не менялось; `config.yaml` всё ещё пример. - Минимальное действие: задать OpenSpec context/rules и completion record: репозиторий, SHA, версия/ревизия контракта, дата и результат проверок. - Владелец: specs и root. Зависимости: соглашение frontend/backend/docs. ### Low #### A-15. ADR отражают план, а не реализованное состояние — `[open]` - Доказательство: `docs/adr/003-tbank-server-side-grpc-boundary.md:19-21`, `004-encrypted-per-user-tbank-token.md:13-15`, `006-auth-session-policy.md:13-15` описывают будущую реализацию; feature docs фиксируют поставленные функции. - Риск: читатель не отличит accepted decision от delivery status. - Минимальное действие: дополнить consequences статусом реализации и ссылкой на feature doc; обновить конфликтующий policy в `docs/adr/007-react-query-fetch-policy.md:9-15`. - Владелец: docs. Зависимости: согласование frontend/backend policy. #### A-16. Локальные frontend guides не имеют явного статуса в общей документации — `[open]` - Доказательство: `frontend/AGENTS.md:19-24` требует читать пять внутренних документов, а корневой `AGENTS.md:17,23` описывает только durable docs. - Риск: агенту неясно, какая документация авторитетна и как поддерживать локальные convention guides. - Минимальное действие: отметить их как frontend-local conventions и связать с development index. - Владелец: root. Зависимости: согласование frontend/docs. #### A-17. Небольшие UX/i18n несоответствия — `[open]` - Доказательство: `frontend/src/features/auth/ui/AuthForm.tsx:84-99` не содержит `autoComplete`; `src/entities/brokerage-account/ui/BrokerageAccountList.tsx:33-38`, `src/entities/portfolio/model/display.ts:20-25`, `src/features/tbank-connection/ui/TbankConnectionSection.tsx:99-104` жёстко используют `ru-RU` при наличии i18n. - Риск: хуже работа password manager и некорректный формат дат при добавлении другой локали. - Минимальное действие: добавить semantic autocomplete и использовать текущий язык i18n в formatters. - Владелец: frontend. Зависимости: решение о дополнительных локалях для второй части. #### A-18. Непоследовательное оформление основной OpenSpec — `[open]` - Доказательство: `specs/openspec/specs/user-authentication/spec.md:1-5` начинается с `## Purpose`, в отличие от остальных спецификаций, например `specs/openspec/specs/tbank-connection/spec.md:1-7`. - Риск: небольшая навигационная и tooling-неоднозначность. - Минимальное действие: добавить `# user-authentication Specification`. - Владелец: specs. Зависимости: нет. ## Подтверждённые сильные стороны - T-Bank tokens шифруются AES-256-GCM с уникальным nonce и authentication tag: `backend/src/tbank/tbank.crypto.ts:16-43`. - Tokens валидируются до сохранения и не возвращаются connection status API: `backend/src/tbank/tbank-connection.service.ts:34-61`, `backend/src/tbank/tbank-connection.controller.ts:35-55`. - JWT algorithm закреплён, а для неизвестного пользователя используется dummy bcrypt hash: `backend/src/auth/jwt.strategy.ts:11-16`, `backend/src/auth/auth.service.ts:50-58`. - Денежная арифметика использует `bigint`, не floating point: `backend/src/tbank/decimal.ts:1-44`. - Frontend использует централизованный типизированный OpenAPI client, не хранит token в persistent storage, а маршруты и страницы имеют loading/error/empty patterns. - Основные OpenSpec scenario-based, охватывают ошибки, cache и безопасность; feature docs содержат пользовательские потоки, API, ограничения и Mermaid diagrams. - Корневые и локальные `AGENTS.md` ясно фиксируют независимые репозитории и SDD intent. ## Результаты воспроизводимых проверок Проверки выполнены 2026-08-13 без запуска `test:cov` и без анализа процентов покрытия. Повторные проверки frontend — 2026-08-14. | Область | Команда | Результат | | --- | --- | --- | | frontend | `./node_modules/.bin/biome check .` | Успешно: 64 файла, замечаний нет. | | frontend | `./node_modules/.bin/tsc -b --noEmit` | Успешно. | | frontend | `./node_modules/.bin/vitest run` | Успешно: 8 files, 25 tests. | | frontend | `./node_modules/.bin/tsc -b && ./node_modules/.bin/vite build` | Успешно; Vite предупреждает о JS chunk 582.99 kB (176.19 kB gzip) после minification. | | frontend | `! rg -i todo src --glob '*.{ts,tsx}'` | Успешно: TODO не найдено. | | backend | `rtk lint "{src,apps,libs,test}/**/*.ts"` | Успешно: ESLint без замечаний. | | backend | `npm test` | Успешно: 8 suites, 22 tests. | | backend | `rtk npm run build` | Успешно: Prisma generate и Nest build. | | specs | `npx openspec validate --all --strict` | Успешно: 7 specs, 0 failures. | | specs | `opsx validate --all --strict` | Не выполнено: `opsx: command not found`. | ## Повторные проверки 2026-08-14 | Область | Команда | Результат | | --- | --- | --- | | frontend | `npm run typecheck` | Успешно. | | frontend | `./node_modules/.bin/biome check .` | Успешно: 81 файл, замечаний нет. | | frontend | `npm run openapi:check` | Успешно: generated artifact соответствует committed snapshot. | | frontend | `npm run deps` | Успешно: `dependency-cruiser`, 84 modules, нарушений нет. | | frontend | `npm run knip` | Успешно. | | frontend | `vitest run` | Успешно: 30 tests (11 files). | | specs | `npx openspec validate --all --strict` | Успешно: 8 specs (добавлен `frontend-ui-resilience`), 0 failures. | | specs | `git status` | Грязный: 16 staged-записей `AD` в `openspec/changes/` — ожидает `git reset`. | ## Рекомендуемый порядок исправлений 1. Создать одну security-focused change для A-01, A-02, A-03 и A-07; добавить связанные concurrent tests из A-13 (backend-часть). Это единственный незакрытый High-блок. 2. Завершить lifecycle A-05: выполнить `git reset` в `specs`, чтобы убрать 16 staged-записей `AD` и вернуть воспроизводимый OpenSpec store. 3. Исправить A-04, A-06, A-14 и A-15 одним documentation/process change, включив multi-repo evidence matrix. 4. Выполнить A-08, A-09 и A-10 отдельными backend-изменениями. 5. Завершить Low-улучшения (A-16, A-17, A-18) после согласования локалей и статуса локальных руководств.