59 KiB
Аудит WealthLedger — 20.08.2026
Исследовательская база: актуальные практики AI-агентов
| Практика | Первичный источник | Следствие для аудита |
|---|---|---|
| Делить постоянные инструкции по области: общий контракт в корне, узкие правила рядом с подсистемой; ближайший файл имеет приоритет. | Codex: AGENTS.md, AGENTS.md standard, GitHub Copilot | Проверить, что корневой AGENTS.md задаёт только межрепозиторные правила, а frontend/, backend/ и specs/ имеют собственные, непротиворечивые инструкции там, где различаются команды и нормы. |
| Ограничивать объём постоянного контекста; переносить локальные процедуры и правила в scoped/JIT-слои. | Codex: 32 KiB по умолчанию, Claude: цель — <200 строк, Copilot: инструкции не длиннее двух страниц, Gemini: JIT-context | Измерить размер и дублирование инструкций; исключить из постоянного слоя длинные справочники, task-specific шаги и нерелевантные правила. |
| Формулировать инструкции конкретно и так, чтобы их можно было проверить: точные пути, команды, предусловия и ожидаемый результат. | Claude: effective instructions, Copilot: validate bootstrap/build/test/lint commands, AGENTS.md standard | Проверить наличие единственного детерминированного local bootstrap и явно перечисленных non-mutating проверок для каждого независимого репозитория. |
| Держать runnable acceptance criteria рядом со спецификацией и трассировать изменение от контракта к проверкам. | Copilot: документировать последовательность validation, Codex: instructions with review rules | Проверить, что OpenSpec change содержит требования, задачи и исполнимые локальные gates, а инструкции связывают реализацию с opsx-verify до завершения работы. |
| Сохранять воспроизводимое окружение: версии рантайма и инструментов, порядок bootstrap, необходимые переменные и подтверждённые обходы. | Copilot: bootstrap и версии, Gemini sandbox: reproducibility | Проверить, можно ли на чистой локальной машине повторить установку и gates без угадывания версий, скрытых сервисов или порядка команд. |
| Разделять поведенческие советы и жёсткое принуждение: критичные запреты должны выполняться permissions/hooks/sandbox, а не только prose. | Claude: settings/hook enforcement vs context, Claude security, Gemini policy engine | Выявить правила, нарушение которых опасно (границы записи, секреты, destructive-команды), и оценить, есть ли для них machine-enforced локальный контроль. |
| Работать с минимальными правами: read-only для исследования, узкие writable roots/allow-rules для реализации, явное подтверждение сети и выхода за границы. | Codex sandboxing, Claude security, Gemini trusted folders | Проверить, что agent workflow не требует broad permissions и что необязательные внешние действия не входят в local read-only MVP. |
| Обеспечивать независимый review после локальных gates: reviewer получает явные критерии и не должен вносить изменения вместо проверки. | GitHub Copilot code review, Claude agent teams: independent lenses | Проверить, что workflow предписывает отдельный review реализации по OpenSpec, diff, тестам и отсутствию лишних изменений после успешной самопроверки. |
| Минимизировать rework: versioned инструкции должны фиксировать только подтверждённые команды, известные ограничения и архитектурные точки входа. | Claude: когда добавлять знания в CLAUDE.md, Copilot CLI: не перегружать постоянные инструкции | Проверить, что документация не заставляет агента повторно искать уже известные команды и не содержит устаревших либо конфликтующих указаний. |
Ограничения доказательности:
- Использованы только официальные документация вендоров и сайт открытого стандарта
AGENTS.md, актуальные на 20.08.2026; это не эмпирическое сравнение качества моделей. - Часть возможностей зависит от конкретного клиента и режима, поэтому аудит оценивает tool-agnostic контракт репозитория, а не предполагает поддержку каждого механизма всеми агентами.
- Scope ограничен локальным read-only MVP: CI/CD, облачное исполнение, deployment и масштабирование намеренно не оцениваются.
Согласованная рамка
Аудит оценивает текущий локальный read-only MVP и качество рабочей области для AI-агентов. Охват: корневые файлы координации, .opencode, frontend, backend, specs и docs. Каталог references/ полностью исключён: его содержимое не читалось и не использовалось как доказательство.
Снимок репозиториев:
| Репозиторий | Branch | Commit | Состояние до аудита |
|---|---|---|---|
frontend |
main |
5168870cf2d5 |
clean |
backend |
main |
e0055414d694 |
clean |
specs |
main |
601a4989913f |
clean |
docs |
main |
680fc03bec7f |
clean |
Не оцениваются и не получают severity: CI/CD, Docker, deployment, hosted security controls, PostgreSQL, HA и масштабирование. Проценты coverage не считаются главной метрикой. Проверяются рискованные сценарии, соответствие OpenSpec, корректность контрактов, воспроизводимость локальных gates и способность независимого агента завершить задачу без повторной переделки.
Статусы доказательности:
- confirmed — воспроизведено командой либо однозначно следует из кода и контракта;
- likely — сильное структурное доказательство, но runtime-сценарий не исполнялся;
- needs verification — гипотеза или применимость внешнего advisory требует отдельной проверки.
Severity присваивается только confirmed-находкам:
- P0 — немедленная потеря/раскрытие данных либо блокировка основного сценария без обхода;
- P1 — неверное поведение основного сценария или системный дефект процесса, регулярно направляющий агента к неверному результату;
- P2 — существенный риск корректности, безопасности, UX или воспроизводимости с ограниченным обходом;
- P3 — локальный долг, hygiene, accessibility или ранний сигнал архитектурного ухудшения.
Итог
Фундамент проекта технически здравый: типизированный OpenAPI-путь, явная T-Bank seam на backend, защищённое хранение токена, структурированные логи, нормальная FSD-декомпозиция и валидный основной OpenSpec store. Однако рабочая область пока не обеспечивает почти безошибочную автономную работу агента. Причина не в объёме инструкций — они компактны — а в том, что несколько ключевых правил существуют только как текст и не совпадают с исполняемыми gates.
P0 не обнаружены. Подтверждены 5 P1, 13 P2 и 8 P3. Самые важные:
- frontend использует не те React Query cache keys, которые затем обновляет и инвалидирует;
- strict TypeScript заявлен, но не включён, поэтому штатный typecheck пропускает 16 ошибок;
- два OpenSpec-root дают разные ответы об активных changes;
- архивирование допускает незавершённые tasks, и такой архив уже существует;
- обязательная backend-проверка
lintизменяет исходники, а отдельные typecheck/e2e gates сейчас неработоспособны.
Матрица состояния
| Область | Оценка | Основание |
|---|---|---|
| Frontend | weak | Сильная структура и тестовая база, но P1 cache-key bug и отсутствующий фактический strict mode. |
| Backend | adequate | Хорошие security/logging seams и unit tests; есть concurrency/cache риски и неработающие read-only gates. |
| API contract | weak | Snapshot и generated types воспроизводимы, но auth status не совпадает с runtime, а freshness относительно backend не проверяется одной локальной командой. |
| Risk-based tests | weak | 46 frontend и 51 backend unit tests проходят; backend e2e не стартует, а ключевые concurrency/cache scenarios отсутствуют. |
| Security local MVP | adequate | Сильные env validation, bcrypt, JWT typing, AES-256-GCM и redaction; остаются concurrency и dependency risks. |
| Архитектура | adequate | Основные seams осмысленны; cache identity и verification/OpenSpec selection остаются распределёнными интерфейсами. |
| SDD и документация | weak | 12/12 основных specs валидны, но source-of-truth раздвоен, lifecycle допускает неполный архив, docs частично устарели. |
| AI-agent readiness | weak | Scoped инструкции хороши, но нет единого read-only verify, машинного enforcement части правил и обязательного независимого review. |
Сильные стороны
Frontend
- FSD-направление
app -> pages -> features -> entities -> shared, публичныеindex.ts, alias@/,dependency-cruiser, Knip и Biome создают хорошую навигацию. - OpenAPI snapshot и generated types закоммичены;
openapi:checkдетерминированно работает без backend и пишет только во временный каталог (frontend/scripts/openapi-check.mjs:6-35). - Auth client хранит access token только в памяти, использует cookie credentials, сериализует refresh и повторяет защищённый запрос один раз (
frontend/src/shared/api/client.ts:20-59). - MSW запрещает необработанные запросы; 15 test files / 46 tests проходят.
frontend/AGENTS.mdкороткий (2 509 bytes), правильно маршрутизирует агента к JIT-документам API/UI/structure/testing.
Backend
- Конфигурация проверяет SQLite URL, origin, port, JWT secret, 32-byte encryption key, T-Bank endpoint/rate и log level (
backend/src/config/env.validation.ts:13-97). - Пароли хэшируются bcrypt cost 12; refresh token хранится как SHA-256 hash; T-Bank token шифруется AES-256-GCM (
backend/src/auth/auth.service.ts:25-59,backend/src/tbank/tbank.crypto.ts:16-44). TbankClient,TbankCrypto,StructuredLogger,RequestContextServiceиPrismaService— полезные глубокие модули: сложность находится за небольшими интерфейсами и используется несколькими flows.- HTTP errors централизованно нормализуются в
application/problem+jsonбез внутренних деталей (backend/src/common/filters/http-exception.filter.ts:11-79). - Логи используют allowlist и исключают authorization, cookie и request body; 17 suites / 51 unit tests проходят.
SDD, docs и contracts
- Основной store
wealth-ledger-specsвалиден:openspec validate --all --store wealth-ledger-specs— 12 passed, 0 failed. CONTEXT.mdдаёт компактный словарь Portfolio, Position, Instrument, Ledger и Future Cash Event.- Feature docs обычно содержат пользовательский поток, API, ограничения и Mermaid-сценарии.
- Статический cross-repo срез показал согласованность 10 controller paths с frontend OpenAPI snapshot и typed usages.
Подтверждённые находки P1
WL-P1-01 — Frontend обновляет неиспользуемые React Query cache keys
Статус: confirmed. Область: correctness / frontend.
В entity hooks пользовательский queryKey указывается до spread apiQueryClient.queryOptions(...), например frontend/src/entities/brokerage-account/api/useBrokerageAccounts.ts:11-17, frontend/src/entities/portfolio/api/usePortfolio.ts:20-28 и frontend/src/entities/brokerage-event/api/useBrokerageEvents.ts:14-23. Установленная библиотека возвращает собственный queryKey (frontend/node_modules/openapi-react-query/src/index.ts:211-219), поэтому последний spread перезаписывает ключ. Strict compiler прямо сообщает TS2783 для пяти hooks.
После connect/delete T-Bank код пишет в ["tbank-connection"] и инвалидирует ["brokerage-accounts"] (frontend/src/features/tbank-connection/ui/TbankConnectionSection.tsx:50-67), тогда как активные queries используют ключи вида ["get", path, init]. UI может остаться в состоянии «не подключено», а счета — не обновиться. Сценарного теста cache update/invalidation нет.
Remediation: сделать cache identity частью единственного интерфейса entity-модуля: получать/экспортировать реальный key из queryOptions либо передавать custom key последним supported-аргументом; добавить integration tests connect/delete → status/accounts refresh.
WL-P1-02 — Заявленный strict TypeScript не является исполняемым инвариантом
Статус: confirmed. Область: correctness / agent gate.
frontend/AGENTS.md:29 требует strict TypeScript, но frontend/tsconfig.app.json:2-25 и frontend/tsconfig.node.json:2-20 не включают strict. Обычный npm run typecheck проходит; npx tsc -p tsconfig.app.json --noEmit --strict воспроизводимо выдаёт 16 ошибок: пять overwritten queryKey, nullable query data, string | undefined и (string | null)[].
Это показательный agent failure: агент может выполнить все написанные команды, получить зелёный gate и передать функционально неверный код.
Remediation: включить strict в обоих tsconfig, исправить все 16 ошибок, оставить диагностический regression test/config check и сделать невозможным расхождение между AGENTS.md и compiler options.
WL-P1-03 — Два OpenSpec-root дают взаимоисключающую картину работы
Статус: confirmed. Область: SDD / source of truth.
Корневое правило называет source of truth specs/openspec (AGENTS.md:5-17), но существует второй openspec/config.yaml:1 и пустой openspec/changes/add-brokerage-account-ledger/. Из корня:
openspec list --jsonпоказывает активныйadd-brokerage-account-ledgerи root workspace;openspec list --store wealth-ledger-specs --jsonпоказывает 0 активных changes и rootspecs.
Даже качественные .opencode инструкции вынуждены повторять правило выбора store во многих командах; selection complexity протекает к каждому caller вместо одного надёжного seam.
Remediation: удалить/перенаправить phantom root; сделать один workspace entrypoint, всегда выбирающий wealth-ledger-specs; добавить machine check, что unscoped и canonical context не расходятся.
WL-P1-04 — Lifecycle позволяет архивировать change с незавершёнными tasks
Статус: confirmed. Область: SDD / evidence integrity.
Архивированный specs/openspec/changes/archive/2026-08-17-redesign-position-cards/tasks.md:33-37 оставляет unchecked archive и cross-repo commit tasks. Это противоречит specs/openspec/specs/project-documentation/spec.md:23-28 и MVP.md:26-28. Причина структурная: .opencode/commands/opsx-archive.md:63-77 разрешает продолжить после подтверждения при незавершённых artifacts/tasks.
Для агента архив перестаёт быть доказательством завершённости; последующий агент не может отличить исключение от процессной ошибки.
Remediation: incomplete tasks должны hard-fail archive либо сам archive должен атомарно закрывать только свои post-actions и записывать проверяемое evidence; исторический change нужно отдельно привести к согласованному состоянию.
WL-P1-05 — Обязательная backend-верификация изменяет пользовательский код
Статус: confirmed. Область: agent safety / reproducibility.
backend/AGENTS.md:7-13 предписывает npm run lint как проверку, но backend/package.json:15 запускает ESLint с --fix. Read-only audit или reviewer, буквально следующий инструкции, может изменить файлы без явного решения. Без --fix ESLint сейчас действительно находит ошибку formatting в backend/src/tbank/upcoming-events.service.ts:40.
Remediation: lint/lint:check сделать non-mutating, автоисправление вынести в lint:fix; добавить один backend verify, который не пишет source/generated output и отчётливо разделяет unit, typecheck, migration и e2e.
Подтверждённые находки P2
WL-P2-01 — Operations cache не имеет capacity/eviction
backend/src/tbank/brokerage-operations.service.ts:33-36 хранит страницы в постоянном Map; stale entry только игнорируется при чтении (:56-59), но не удаляется, а каждый уникальный запрос записывает новый entry (:79, :104). Cache key включает клиентские query/cursor (:56), cursor допускает 4 096 символов, limit — до 1 000 (backend/src/tbank/brokerage-operation.dto.ts:46-58). Даже в локальном MVP ошибочный/зацикленный клиент может неограниченно наращивать память процесса.
Remediation: bounded LRU/TTL с удалением stale entries, лимит веса/числа страниц, canonical upstream cursor и тест на большое число уникальных ключей.
WL-P2-02 — Параллельная регистрация может вернуть 500 вместо 409
backend/src/auth/auth.service.ts:30-42 делает check-then-create, а unique constraint находится в backend/prisma/schema.prisma:11-18. Две регистрации одного email могут одновременно пройти lookup; проигравший create выбросит Prisma P2002, который общий filter преобразует в 500. Последовательный e2e (backend/test/app.e2e-spec.ts:62-80) гонку не проверяет.
Remediation: считать unique constraint источником истины, маппить P2002 в 409 EMAIL_ALREADY_REGISTERED, добавить parallel integration test.
WL-P2-03 — Refresh rotation не атомарна
Два запроса могут прочитать одинаковый hash (backend/src/auth/auth.service.ts:75-96), затем оба записать разные новые hashes (:148-166). Token из ответа, проигравшего last-write, будет воспринят как reuse и отзовёт Session. Ограничение явно документировано (docs/features/authentication.md:17-19), поэтому это не скрытое нарушение MVP, но остаётся техническим риском correctness.
Remediation: compare-and-swap updateMany по session.id + oldHash либо транзакционный adapter; ровно один refresh должен победить, второй — получить определённый idempotent/unauthorized результат без случайного отзыва победителя.
WL-P2-04 — OpenAPI и runtime расходятся по success status auth endpoints
Register/login/refresh помечены @ApiOkResponse (backend/src/auth/auth.controller.ts:51-76) и committed snapshot содержит только 200, но Nest POST без @HttpCode(200) возвращает 201; e2e ожидает 201 (backend/test/app.e2e-spec.ts:34-37, :51-52). Frontend types строятся по ошибочному contract.
Remediation: выбрать 200 или 201, согласовать controller decorator, tests, OpenAPI snapshot и frontend response types; добавить runtime-vs-snapshot contract check.
WL-P2-05 — Backend e2e gate не запускает Jest
npm run test:e2e (backend/package.json:20) воспроизводимо падает на prisma migrate deploy с Schema engine error до старта тестов. Unit tests не компенсируют отсутствие реального HTTP/DB gate для auth, migrations и contract.
Remediation: отдельно диагностировать Prisma/SQLite schema-engine failure; e2e должен использовать уникальную временную БД, сам создавать и удалять её и возвращать диагностируемую ошибку.
WL-P2-06 — Backend не имеет проходящего standalone typecheck
В scripts отсутствует typecheck; npx tsc --noEmit падает, потому что backend/src/tbank/tbank-connection.service.spec.ts:20-27 передаёт неполный mock как TbankClient. Build исключает specs, поэтому production build может быть зелёным при типовых ошибках тестов.
Remediation: добавить отдельные app/test tsconfig либо typed adapter fake; включить оба в non-mutating verify.
WL-P2-07 — Background refetch скрывает валидный cached snapshot
frontend/src/pages/accounts/ui/AccountsPage.vm.ts:7-15 возвращает loading при любом isFetching. frontend/src/pages/account/ui/AccountPage.vm.ts:16-23 ставит error/fetching выше существующих data. Во время background refresh пользователь теряет последний валидный финансовый снимок; при refetch error вместо stale data получает full error state, хотя docs описывают stale-snapshot UX.
Remediation: initial state определять через isPending/absence of data; cached data оставлять видимыми, а background error показывать как stale/non-blocking state.
WL-P2-08 — Cold-start refresh оставляет unhandled rejection
frontend/src/features/auth/model/auth.tsx:66-76 запускает promise через void, имеет .then и .finally, но не .catch. Transport exception превращается в unhandled rejection; ready всё равно становится true без контролируемой диагностики. Тест frontend/src/features/auth/model/auth.test.tsx:11-35 покрывает только успешное восстановление.
Remediation: явно нормализовать network failure в unauthenticated state, исключить unhandled rejection и добавить cold-start failure test.
WL-P2-09 — Локальный contract gate проверяет snapshot только против generated types
frontend/scripts/openapi-check.mjs:6-35 сравнивает generated schema с committed snapshot. Текущий backend документ извлекается только ручным mutating openapi:sync (frontend/scripts/openapi-sync.mjs:6-41). Поэтому auth status drift уже существует, хотя frontend openapi:check зелёный.
Remediation: workspace-level read-only command должен поднять backend с детерминированным test env, получить /api/docs-json, canonicalize и сравнить со snapshot, ничего не обновляя.
WL-P2-10 — Onboarding и постоянная документация заметно отстают от кода
frontend/README.md:1-77— Vite template, описывающий ESLint вместо фактического Biome/FSD/OpenAPI workflow;backend/README.md:24-98— Nest template сnpm install, без env/Prisma/API map;docs/architecture/README.md:1-3иdocs/development/README.md:1-3— placeholders;docs/features/tbank-connection.md:38-40утверждает, что реализована только проверка токена, хотя accounts/portfolio/ledger/events уже документированы и реализованы;MVP.md:18-24не отражает ledger/events и использует future-tense для уже работающей T-Bank seam.
Stale docs повышают rework: следующий агент вынужден повторно устанавливать факты либо принимает неверное ограничение за source of truth.
Remediation: agent-oriented entrypoints для frontend/backend, фактическая architecture map и полный local workflow; при закрытии change проверять stale future-tense и delivery map.
WL-P2-11 — Reproducibility frontend и backend асимметрична
Frontend фиксирует Node 22 в frontend/package.json:6-8 и .nvmrc; backend не имеет engines/.nvmrc, а README предлагает npm install (backend/README.md:28-32) вместо lockfile-safe npm ci. Проверка проходила на Node v22.21.0, npm 10.9.4, но repo не гарантирует это следующему агенту.
Remediation: единая Node/npm policy, npm ci, перечисленные env/migration/bootstrap steps и machine check версии.
WL-P2-12 — Актуальный npm audit сообщает high advisories
Онлайн-проверка 20.08.2026:
- frontend: прямой
react-router@7.9.6(frontend/package.json:40) попадает в high advisories, включая GHSA-2w69-qvjg-hvjx; registry предлагает переход на 7.18.2 вне текущего exact range; - backend:
deepmerge-ts@7.1.5черезprisma -> @prisma/configпопадает в GHSA-ggr8-5vv4-36mx;npm auditсчитает цепочку тремя high findings.
Уязвимые версии подтверждены. Применимость exploit требует проверки: frontend — client-only SPA без SSR/RSC и с внутренними routes; backend chain в основном Prisma tooling/config, а не user-controlled runtime merge.
Remediation: отдельный dependency change с анализом применимости и regression tests; не использовать автоматический npm audit fix --force без проверки breaking changes.
WL-P2-13 — Нет единого интерфейса локальной верификации для четырёх Git-root
Корень не является Git-репозиторием (AGENTS.md:19-22), а каждый subrepo имеет отдельный AGENTS.md. В разных agent clients instruction discovery начинается от выбранного project/Git root; запуск из workspace и запуск из subrepo не гарантируют одинаковую цепочку root + local guidance. Единой read-only команды, которая выбирает canonical OpenSpec store, запускает изменённые repo gates, cross-contract check и проверяет лишний diff, нет.
Remediation: тонкий tool-agnostic workspace verifier/manifest: явная карта репозиториев, canonical store, non-mutating команды, changed-repo selection, expected artifacts и финальный clean-diff check. Tool-specific skills остаются адаптерами, а не вторым источником правил.
Подтверждённые находки P3
WL-P3-01 — Unknown request fields молча отбрасываются
ValidationPipe({ transform: true, whitelist: true }) в backend/src/main.ts:28 не включает forbidNonWhitelisted. Опечатка поля может выглядеть как успешный запрос. Для typed clients это редкость, но для ручной/agent диагностики ухудшает обратную связь.
Remediation: включить forbidNonWhitelisted после contract test на ожидаемый problem response либо явно документировать permissive policy.
WL-P3-02 — В shared i18n есть import cycle
frontend/src/shared/i18n/index.ts:13 re-export-ит ./zod, а frontend/src/shared/i18n/zod.ts:3 импортирует .. Graph analysis подтвердил двухфайловый cycle; текущий dependency-cruiser проверяет только направление слоёв (frontend/dependency-cruiser.cjs:1-27), но не cycles.
Remediation: импортировать нужный leaf напрямую и добавить no-circular rule в dependency-cruiser.
WL-P3-03 — Public API правило FSD уже нарушено
frontend/AGENTS.md:27 требует cross-slice imports через public index.ts, но frontend/src/features/auth/ui/AuthForm.tsx:9 использует относительный deep path ../../../shared/i18n. Текущий deps gate это пропускает.
Remediation: перейти на public alias и расширить dependency rule так, чтобы deep cross-slice imports не проходили.
WL-P3-04 — Базовая accessibility metadata не соответствует русскому UI
frontend/index.html:2 задаёт lang="en"; T-Bank loading — обычный Text без status/live semantics (frontend/src/features/tbank-connection/ui/TbankConnectionSection.view.tsx:4-7).
Remediation: задать фактический язык документа и общий accessible loading/status primitive.
WL-P3-05 — Один frontend chunk уже превышает стандартный warning threshold
Production Vite build прошёл, но сформировал один JS chunk около 620 kB minified / 186 kB gzip и выдал warning >500 kB. Все pages импортируются статически (frontend/src/app/routes/AppRoutes.tsx:3-10), vite.config.ts:7-10 не задаёт splitting. Для локального MVP это не блокер, но дальнейший рост ухудшит startup.
Remediation: lazy-load route boundaries и закрепить измеримый bundle regression budget без преждевременного дробления vendor chunks.
WL-P3-06 — Coverage запускается без regression thresholds
frontend/package.json:16,22 запускает coverage, но frontend/vitest.config.ts:7-11 содержит только reporters. Coverage не должен быть главной метрикой, однако минимальный baseline полезен как сигнал внезапного исчезновения проверяемых branches.
Remediation: после добавления рискованных сценариев зафиксировать умеренный baseline/ratchet; решения о качестве по-прежнему принимать по scenarios, не по одному проценту.
WL-P3-07 — Frontend .env закоммичен и не игнорируется
frontend/.env tracked и содержит только локальный VITE_API_BASE_URL; секрета сейчас нет, а Vite vars всё равно публичны. Но .gitignore не исключает .env, поэтому следующий агент может случайно закоммитить новое значение вместо изменения .env.example.
Remediation: оставить tracked .env.example, игнорировать рабочие .env* с явным исключением examples и добавить secret/hygiene check.
WL-P3-08 — Backend хранит tracked пустой runtime DB вне ignore pattern
backend/dev.db tracked, имеет размер 0 bytes. .gitignore:61-62 исключает только prisma/*.db, поэтому root dev.db не защищён от будущего заполнения user/session/token data.
Remediation: удалить пустой artifact отдельным согласованным change, игнорировать DB files во всех runtime locations и проверять, что fixtures создаются во временном каталоге.
Архитектурная оценка
Для оценки использовалась модель deep modules: хороший модуль скрывает заметную сложность за небольшим устойчивым интерфейсом; слабый seam заставляет каждого caller знать детали выбора, последовательности или внутреннего состояния.
Удачные seams
TbankClientскрывает gRPC transport, metadata, rate/queue policy и преобразование upstream errors; замена транспорта локализована в одном adapter boundary.TbankCryptoпредоставляет малый интерфейс encrypt/decrypt поверх корректного authenticated encryption и не разносит формат ciphertext по feature-коду.StructuredLoggerиRequestContextServiceцентрализуют allowlist/redaction и correlation context.PrismaServiceизолирует жизненный цикл DB adapter; repository-specific data access остаётся в сервисах предметной области.- Frontend
apiClientцентрализует bearer token, cookie credentials, single-flight refresh и retry; generated OpenAPI types дают единую транспортную модель.
Слишком распределённые интерфейсы
| Интерфейс | Где протекает сложность | Последствие |
|---|---|---|
| React Query cache identity | Каждый entity hook комбинирует custom key с generated queryOptions, а mutation UI отдельно угадывает ключи. |
Уже привело к WL-P1-01; refactor или новый endpoint легко повторит дефект. |
| Выбор OpenSpec store | Canonical store задаётся prose и флагом, тогда как второй root остаётся распознаваемым CLI. | Результат зависит от cwd/команды, а не от предметного состояния проекта. |
| Workspace verification | Frontend имеет широкий verify, backend — набор неодинаковых scripts, cross-contract — ручной, Git roots независимы. |
Агент не может получить один однозначный read-only verdict. |
| Completion evidence | Tasks, archive command, Git commits и результаты gates не образуют одного проверяемого record. | Архив не гарантирует, что работа закончена и проверена. |
TbankConnectionModule пока не следует механически дробить: один adapter seam для одного провайдера глубже, чем россыпь формальных abstractions. Разделение оправдано только при появлении независимых вариантов transport/provider или если модуль нельзя тестировать без знания нескольких несвязанных политик. Практический deletion test: удаление T-Bank должно затронуть feature/module wiring и клиентские T-Bank slices, но не auth, logging, portfolio domain primitives или общий transport.
Структурный граф
Graphify-анализ выполнен по 319 файлам рабочей области после исключения references/, .git, node_modules, build/coverage outputs, env, сертификатов и DB. Итоговый граф содержит 1 307 nodes, 1 816 edges и 92 communities. Самые связные прикладные узлы — TbankClient, TbankCrypto, StructuredLogger, PrismaService, RequestContextService и frontend useAuth; это подтверждает, что transport/security/context seams являются реальными центрами системы, а не только заявлены в документации.
Единственный подтверждённый source-level cycle вне generated Prisma-кода — frontend/src/shared/i18n/index.ts -> zod.ts -> index.ts (WL-P3-02). Generated Prisma nodes намеренно не используются как основание для архитектурного refactor: их центральность является свойством генератора. Семантическая связь OpenAPI change/spec с frontend API layer — полезный сигнал трассируемости, но сейчас она не подкреплена runtime-vs-snapshot gate (WL-P2-09).
Готовность к работе AI-агентов
Что уже помогает агенту
- Корневой
AGENTS.mdкоротко объясняет четыре Git-root, роль каждого репозитория и запрет смешивать commits. - Frontend instructions хорошо используют progressive disclosure: базовые правила остаются короткими, конкретные convention guides читаются по задаче.
- OpenSpec specs scenario-based и дают гораздо лучший intent, чем issue title или список файлов.
- Generated API types, deterministic frontend snapshot check, MSW strict mode, dependency-cruiser и Knip уменьшают пространство скрытых ошибок.
- Локальная модель прав не требует сети или внешних credentials для большинства статических проверок.
Почему агент всё ещё будет ошибаться
| Ожидание | Текущая реальность | Типичная ошибка агента |
|---|---|---|
| Инструкция соответствует gate | «strict» написан, но compiler strict не запускается. | Агент передаёт 16 скрытых type errors и функциональный cache bug. |
| Verification не изменяет diff | Backend lint всегда использует --fix. |
Reviewer незаметно меняет пользовательский код и смешивает audit с implementation. |
| Source of truth один | CLI видит два OpenSpec roots. | Change создаётся/проверяется не в том store. |
| Archived означает complete | Archive допускает unchecked tasks. | Новый агент принимает незавершённую работу за поставленную. |
| Все обязательные gates runnable | Backend typecheck отсутствует, e2e падает до Jest, contract freshness ручная. | Агент либо сдаёт непроверенный результат, либо импровизирует несовместимую команду. |
| Docs описывают фактическое состояние | README/templates и feature/MVP содержат устаревшие сведения. | Агент повторно исследует проект или реализует уже существующую возможность. |
| Review независим | Обязательной fresh-agent проверки по spec и standards нет. | Авторская ошибка проходит вместе с авторской интерпретацией acceptance criteria. |
Целевой локальный workflow
- Агент из одного entrypoint определяет canonical OpenSpec store и затронутые Git-root; двусмысленность считается ошибкой.
- До реализации существует согласованный change: requirements, scenarios, design decisions, task-to-repo map и рискованные acceptance tests.
- Реализация затрагивает только перечисленные repos; generated artifacts меняются только явной generate-командой.
- Один read-only verifier выбирает нужные checks: format/lint check, app+test typecheck, unit/risk tests, OpenSpec validation, runtime-vs-snapshot contract и clean-diff check.
- Fresh agent получает OpenSpec, diff и evidence и отдельно проверяет две оси: Spec (все requirements/scenarios реализованы) и Standards (архитектура, безопасность, repo rules, лишние изменения). Для auth/crypto/data-loss changes добавляется security lens.
- Archive разрешён только после зелёных machine gates, закрытых tasks и записанного evidence по каждому Git-root. Reviewer ничего автоматически не исправляет.
Этот workflow не требует hosted/CI инфраструктуры: он является локальным контрактом команд и артефактов. Главное свойство — одинаковый результат у автора и у независимого агента, а не количество текста в инструкциях.
Предлагаемые будущие OpenSpec changes
Ниже — группировка для будущего планирования; changes в рамках аудита не создавались.
| Предлагаемый change | Находки | Impact / effort | Минимальные acceptance criteria |
|---|---|---|---|
stabilize-frontend-query-and-strict-contracts |
P1-01, P1-02, P2-07, P2-08 | high / M | Реальные query keys имеют одного владельца; connect/delete обновляют status/accounts; strict включён для app и tooling; cold-start/refetch scenarios проходят. |
harden-openspec-authority-and-lifecycle |
P1-03, P1-04 | high / S–M | CLI из workspace и specs выбирает один store; phantom root отсутствует; incomplete tasks блокируют archive; completion evidence проверяется машинно. |
make-local-verification-agent-safe |
P1-05, P2-05, P2-06, P2-09, P2-11, P2-13 | high / M | Один non-mutating workspace command; backend lint/check разделён с fix; app+test typecheck и e2e проходят; backend OpenAPI сравнивается со snapshot; финальный diff содержит только ожидаемые файлы. |
harden-auth-and-cache-concurrency |
P2-01, P2-02, P2-03 | high / M | Bounded cache; parallel registration стабильно даёт один success и один 409; refresh CAS допускает одного победителя без случайного revoke. |
align-api-contract-and-client-resilience |
P2-04, P2-07, P2-08 | high / M | Runtime/status/OpenAPI/types совпадают; stale data остаются видимыми; transport failures не создают unhandled rejection. |
refresh-project-documentation-and-agent-entrypoints |
P2-10, P2-11, P2-13 | medium / M | README не являются templates; architecture/data-flow и exact bootstrap актуальны; authoritative/JIT docs явно разделены; команды проверены на чистой локальной среде. |
address-dependency-advisories-and-repo-hygiene |
P2-12, P3-07, P3-08 | medium / S–M | Applicability обоих advisories документирована; безопасные upgrades имеют regression tests; .env/DB policy защищена ignore/check без удаления нужных examples. |
tighten-frontend-quality-guardrails |
P3-02–P3-06 | low–medium / S–M | Нет source cycles/deep imports; lang и loading semantics корректны; route splitting имеет измеримый критерий; coverage threshold используется только как regression signal. |
Зависимости: сначала устранить двусмысленность OpenSpec lifecycle, затем закрепить единый verifier; feature/security changes должны опираться уже на эти два process seams. P3 hygiene можно выполнять независимо, но не смешивать с функциональным change без явной причины.
Результаты воспроизводимых проверок
Среда аудита: Node v22.21.0, npm 10.9.4, OpenSpec 1.9.0.
| Область | Команда | Результат |
|---|---|---|
| frontend | npm run typecheck |
pass; текущий non-strict gate зелёный. |
| frontend | npx tsc -p tsconfig.app.json --noEmit --strict |
fail: 16 type errors; в том числе 5 TS2783 по queryKey. |
| frontend | npm run lint |
pass. |
| frontend | npm run openapi:check |
pass; generated types совпадают с committed snapshot. |
| frontend | npm run test:run |
pass: 15 files, 46 tests. |
| frontend | npm run knip |
pass. |
| frontend | npm run deps |
pass: 97 modules, 293 dependencies, violations нет. |
| frontend | npm run build |
pass; JS chunk около 620 kB / 186 kB gzip, warning >500 kB. |
| backend | npm test -- --runInBand |
pass: 17 suites, 51 tests. |
| backend | npx eslint "{src,apps,libs,test}/**/*.ts" |
fail: одна Prettier error в upcoming-events.service.ts:40; source не изменён. |
| backend | npx tsc --noEmit |
fail: incomplete typed TbankClient mock в test compile. |
| backend | npm run test:e2e |
fail до Jest: Prisma Schema engine error. |
| backend | npx prisma validate |
pass. |
| backend | node test/runtime-check.cjs |
pass. |
| specs | openspec validate --all --store wealth-ledger-specs |
pass: 12 specs, 0 failures. |
| workspace | openspec list --json vs openspec list --store wealth-ledger-specs --json |
fail consistency: phantom active change vs 0 canonical changes. |
| dependencies | npm audit --json отдельно в frontend/backend |
frontend: 1 high package; backend: 3 high findings; применимость помечена needs verification. |
| repositories | git status --short в четырёх subrepos |
clean на входе в аудит. |
Зелёные unit tests доказывают только покрытые сценарии; они не отменяют красные strict/typecheck/e2e/contract gates. npm audit выполнялся с сетью только для получения актуального advisory snapshot и не менял lockfiles.
Изменения относительно audit.md от 13–14.08.2026
Новый отчёт является самостоятельным snapshot, а не дописыванием старого.
- Старые утверждения о staged
ADвspecsи отсутствии OpenSpec/opsxtooling больше не верны: все четыре subrepo clean, OpenSpec 1.9.0 доступен, canonical store проходит 12/12. - A-01 (неатомарный refresh), A-04 (устаревшая T-Bank документация), A-06/A-14 (architecture/workflow/evidence) и A-07 (registration race) сохраняются и переоценены как WL-P2-03, P2-10/P2-13 и P2-02.
- Hosted-specific A-03 намеренно не переоценивался: по согласованной рамке это локальный MVP, hosted/deployment требования исключены.
- Lifecycle-риск A-05 изменил форму: Git index теперь clean, но canonical store раздвоен с phantom root, а один архив содержит unchecked tasks.
- Frontend test/FSD foundation стала сильнее, однако строгая компиляция впервые выявила cache-key defect и 16 пропущенных типовых ошибок.
- Впервые отдельно проверены backend read-only gates, runtime-vs-snapshot contract, актуальные dependency advisories и пригодность workflow для независимого AI review.
Приоритетный backlog
| Порядок | Действие | Находки | Impact | Effort |
|---|---|---|---|---|
| 1 | Исправить query identity и реально включить strict TypeScript вместе со сценарными тестами. | P1-01, P1-02 | high | M |
| 2 | Установить один OpenSpec root и запретить archive с незавершёнными tasks. | P1-03, P1-04 | high | S–M |
| 3 | Сделать backend/workspace verification полностью read-only и проходящим. | P1-05, P2-05, P2-06, P2-09, P2-13 | high | M |
| 4 | Закрыть registration/refresh races и ограничить operations cache. | P2-01–P2-03 | high | M |
| 5 | Синхронизировать runtime OpenAPI и улучшить cached/cold-start UX. | P2-04, P2-07, P2-08 | high | M |
| 6 | Обновить README, architecture/development docs, MVP и agent evidence workflow. | P2-10, P2-11, P2-13 | medium | M |
| 7 | Проверить advisories и закрепить .env/DB hygiene. |
P2-12, P3-07, P3-08 | medium | S–M |
| 8 | Закрыть локальные frontend guardrails. | P3-02–P3-06 | low–medium | S–M |
Ограничения и needs verification
- Не выполнялись live-вызовы T-Bank, browser E2E, нагрузочные тесты и destructive race experiments на пользовательских данных.
- Причина Prisma schema-engine failure не диагностировалась: аудит подтверждает красный gate, но не приписывает ему неподтверждённый root cause.
- Runtime-влияние React Router и
deepmerge-tsadvisories на текущие call paths не доказано; подтверждены только версии и advisory ranges. - Cross-repo path alignment проверен статически; полный backend-runtime-to-generated-client contract test отсутствует и сам является находкой.
- Graphify semantic extraction использовалась как навигация и cross-check, а не как единственное доказательство severity. Точная token-cost метрика subagents была недоступна, поэтому не оценивалась.
references/не читалась и не индексировалась. Hosted, deployment, CI/CD, Docker, PostgreSQL, HA и scale остаются вне рамки.
Заключение
WealthLedger — не технически плохой проект: его основные domain/transport/security seams лучше среднего для локального MVP, а typed contract, FSD, unit tests и scenario-based specs дают хороший фундамент. Главный разрыв находится между заявленным процессом и машинно проверяемым процессом. Пока агент может выполнить написанные команды и всё равно получить неверные cache updates, пропустить strict errors, выбрать не тот OpenSpec root или изменить код во время review.
Первая цель улучшений — не добавлять больше prose, а углубить три интерфейса: один владелец query/cache identity, один canonical OpenSpec lifecycle и один non-mutating local verifier с независимым review. После этого проект станет существенно ближе к требованию «агент выполняет задачу практически без ошибок и без переделки».