docs: add old audit file

This commit is contained in:
2026-08-24 10:33:29 +03:00
parent b91dfe66e0
commit 5bf5c4e774
2 changed files with 237 additions and 0 deletions
+236
View File
@@ -0,0 +1,236 @@
# Аудит качества 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) после согласования локалей и статуса локальных руководств.