diff --git a/AGENTS.md b/AGENTS.md index 4ba913c..e193ac7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,3 +15,4 @@ git status --short - `adr/` - долгоживущие решения, `architecture/` - устройство системы, `development/` - процесс разработки, `features/` - фактическое поведение завершенных фич. - Для диаграмм использовать Mermaid в Markdown. Feature-документация описывает пользовательский поток, API-сценарий и ограничения. - Не хранить OpenSpec-артефакты, код приложений или секреты; требования и task-план остаются в `../specs/openspec`. +- Для задачи по Audit ID, продолжения remediation или выбора следующего улучшения сначала прочитать [`development/audit-remediation.md`](development/audit-remediation.md); canonical OpenSpec store — `wealth-ledger-specs`. diff --git a/audits/README.md b/audits/README.md new file mode 100644 index 0000000..6e0ff7c --- /dev/null +++ b/audits/README.md @@ -0,0 +1,7 @@ +# Technical audits + +Этот каталог содержит неизменяемые, versioned snapshots технических аудитов. Snapshot фиксирует доказательства и выводы на свою дату; его не обновляют при последующих исправлениях. + +Текущий remediation status, следующий change и evidence завершённых работ находятся в [audit remediation registry](../development/audit-remediation.md). + +- [Аудит 20.08.2026](audit-20-08-2026.md) diff --git a/audits/audit-20-08-2026.md b/audits/audit-20-08-2026.md new file mode 100644 index 0000000..f4da87d --- /dev/null +++ b/audits/audit-20-08-2026.md @@ -0,0 +1,438 @@ +# Аудит WealthLedger — 20.08.2026 + +## Исследовательская база: актуальные практики AI-агентов + +| Практика | Первичный источник | Следствие для аудита | +| --- | --- | --- | +| Делить постоянные инструкции по области: общий контракт в корне, узкие правила рядом с подсистемой; ближайший файл имеет приоритет. | [Codex: AGENTS.md](https://learn.chatgpt.com/docs/agent-configuration/agents-md), [AGENTS.md standard](https://agents.md/), [GitHub Copilot](https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions) | Проверить, что корневой `AGENTS.md` задаёт только межрепозиторные правила, а `frontend/`, `backend/` и `specs/` имеют собственные, непротиворечивые инструкции там, где различаются команды и нормы. | +| Ограничивать объём постоянного контекста; переносить локальные процедуры и правила в scoped/JIT-слои. | [Codex: 32 KiB по умолчанию](https://learn.chatgpt.com/docs/agent-configuration/agents-md), [Claude: цель — <200 строк](https://code.claude.com/docs/en/memory), [Copilot: инструкции не длиннее двух страниц](https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions), [Gemini: JIT-context](https://geminicli.com/docs/cli/gemini-md/) | Измерить размер и дублирование инструкций; исключить из постоянного слоя длинные справочники, task-specific шаги и нерелевантные правила. | +| Формулировать инструкции конкретно и так, чтобы их можно было проверить: точные пути, команды, предусловия и ожидаемый результат. | [Claude: effective instructions](https://code.claude.com/docs/en/memory), [Copilot: validate bootstrap/build/test/lint commands](https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions), [AGENTS.md standard](https://agents.md/) | Проверить наличие единственного детерминированного local bootstrap и явно перечисленных non-mutating проверок для каждого независимого репозитория. | +| Держать runnable acceptance criteria рядом со спецификацией и трассировать изменение от контракта к проверкам. | [Copilot: документировать последовательность validation](https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions), [Codex: instructions with review rules](https://learn.chatgpt.com/docs/agent-configuration/agents-md) | Проверить, что OpenSpec change содержит требования, задачи и исполнимые локальные gates, а инструкции связывают реализацию с `opsx-verify` до завершения работы. | +| Сохранять воспроизводимое окружение: версии рантайма и инструментов, порядок bootstrap, необходимые переменные и подтверждённые обходы. | [Copilot: bootstrap и версии](https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions), [Gemini sandbox: reproducibility](https://geminicli.com/docs/cli/sandbox/) | Проверить, можно ли на чистой локальной машине повторить установку и gates без угадывания версий, скрытых сервисов или порядка команд. | +| Разделять поведенческие советы и жёсткое принуждение: критичные запреты должны выполняться permissions/hooks/sandbox, а не только prose. | [Claude: settings/hook enforcement vs context](https://code.claude.com/docs/en/memory), [Claude security](https://code.claude.com/docs/en/security), [Gemini policy engine](https://geminicli.com/docs/reference/policy-engine/) | Выявить правила, нарушение которых опасно (границы записи, секреты, destructive-команды), и оценить, есть ли для них machine-enforced локальный контроль. | +| Работать с минимальными правами: read-only для исследования, узкие writable roots/allow-rules для реализации, явное подтверждение сети и выхода за границы. | [Codex sandboxing](https://learn.chatgpt.com/docs/sandboxing), [Claude security](https://code.claude.com/docs/en/security), [Gemini trusted folders](https://geminicli.com/docs/cli/trusted-folders/) | Проверить, что agent workflow не требует broad permissions и что необязательные внешние действия не входят в local read-only MVP. | +| Обеспечивать независимый review после локальных gates: reviewer получает явные критерии и не должен вносить изменения вместо проверки. | [GitHub Copilot code review](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/request-a-code-review/use-code-review), [Claude agent teams: independent lenses](https://code.claude.com/docs/en/agent-teams) | Проверить, что workflow предписывает отдельный review реализации по OpenSpec, diff, тестам и отсутствию лишних изменений после успешной самопроверки. | +| Минимизировать rework: versioned инструкции должны фиксировать только подтверждённые команды, известные ограничения и архитектурные точки входа. | [Claude: когда добавлять знания в CLAUDE.md](https://code.claude.com/docs/en/memory), [Copilot CLI: не перегружать постоянные инструкции](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/comparing-cli-features) | Проверить, что документация не заставляет агента повторно искать уже известные команды и не содержит устаревших либо конфликтующих указаний. | + +Ограничения доказательности: + +- Использованы только официальные документация вендоров и сайт открытого стандарта `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. Самые важные: + +1. frontend использует не те React Query cache keys, которые затем обновляет и инвалидирует; +2. strict TypeScript заявлен, но не включён, поэтому штатный typecheck пропускает 16 ошибок; +3. два OpenSpec-root дают разные ответы об активных changes; +4. архивирование допускает незавершённые tasks, и такой архив уже существует; +5. обязательная 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 и root `specs`. + +Даже качественные `.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](https://github.com/advisories/GHSA-2w69-qvjg-hvjx); registry предлагает переход на 7.18.2 вне текущего exact range; +- backend: `deepmerge-ts@7.1.5` через `prisma -> @prisma/config` попадает в [GHSA-ggr8-5vv4-36mx](https://github.com/advisories/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 + +1. Агент из одного entrypoint определяет canonical OpenSpec store и затронутые Git-root; двусмысленность считается ошибкой. +2. До реализации существует согласованный change: requirements, scenarios, design decisions, task-to-repo map и рискованные acceptance tests. +3. Реализация затрагивает только перечисленные repos; generated artifacts меняются только явной generate-командой. +4. Один read-only verifier выбирает нужные checks: format/lint check, app+test typecheck, unit/risk tests, OpenSpec validation, runtime-vs-snapshot contract и clean-diff check. +5. Fresh agent получает OpenSpec, diff и evidence и отдельно проверяет две оси: **Spec** (все requirements/scenarios реализованы) и **Standards** (архитектура, безопасность, repo rules, лишние изменения). Для auth/crypto/data-loss changes добавляется security lens. +6. 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/`opsx` tooling больше не верны: все четыре 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-ts` advisories на текущие 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. После этого проект станет существенно ближе к требованию «агент выполняет задачу практически без ошибок и без переделки». diff --git a/development/README.md b/development/README.md index 8e34892..5191c56 100644 --- a/development/README.md +++ b/development/README.md @@ -1,3 +1,5 @@ # Development Здесь размещаются устойчивые правила разработки, локальной проверки и поставки. Требования и планы изменений остаются в `../specs/openspec`. + +- [Audit remediation registry](audit-remediation.md) — текущий статус findings, следующий OpenSpec change и evidence завершённых улучшений. diff --git a/development/audit-remediation.md b/development/audit-remediation.md new file mode 100644 index 0000000..05c0986 --- /dev/null +++ b/development/audit-remediation.md @@ -0,0 +1,81 @@ +# Audit remediation registry + +## Назначение + +Это versioned реестр текущего состояния remediation для findings из [технического аудита 20.08.2026](../audits/audit-20-08-2026.md). Аудит — неизменяемый source of facts; этот документ хранит только приоритет, статус, путь продолжения и evidence последующих решений. + +## Правила работы + +- Каждая audit-derived запись имеет один статус: `planned`, `active`, `done`, `deferred` или `accepted`. +- Одновременно допускается не более одного `active` remediation change. При отсутствии active change раздел «Следующая задача» содержит ровно один рекомендуемый change. +- `done` требует даты, archive path OpenSpec change, commit для каждого затронутого Git-репозитория и точных non-mutating verification commands с observed results. +- `deferred` и `accepted` требуют даты, rationale и review trigger либо ссылки на явное решение владельца. +- При смене статуса обновляется только этот реестр; versioned audit snapshot не редактируется. + +## Текущее состояние workflow + +- Bootstrap change: [`2026-08-23-establish-audit-remediation-workflow`](../../specs/openspec/changes/archive/2026-08-23-establish-audit-remediation-workflow/) — `archived`; это process foundation и не закрывает ни одного Audit ID. +- Active audit remediation change: отсутствует. + +## Следующая задача + +Создать в canonical store `wealth-ledger-specs` change `stabilize-frontend-query-and-strict-contracts` для **WL-P1-01** и **WL-P1-02**. Он должен исправить query cache identity, включить strict TypeScript и устранить все 16 выявленных strict errors с поведенческими regression tests. + +## Реестр findings + +### P1 + +| Audit ID | Цель | Репозитории | Статус | Связанный change | Следующее действие | Terminal evidence | +| --- | --- | --- | --- | --- | --- | --- | +| WL-P1-01 | Один владелец React Query cache identity. | frontend | planned | `stabilize-frontend-query-and-strict-contracts` (ещё не создан) | Создать change после bootstrap. | — | +| WL-P1-02 | Сделать strict TypeScript исполняемым gate и устранить 16 ошибок. | frontend | planned | `stabilize-frontend-query-and-strict-contracts` (ещё не создан) | Выполнить вместе с WL-P1-01. | — | +| WL-P1-03 | Установить единственный canonical OpenSpec root. | specs | planned | `harden-openspec-authority-and-lifecycle` (ещё не создан) | Запланировать после P1 frontend change. | — | +| WL-P1-04 | Сделать archive lifecycle доказательством завершённости. | specs, docs | planned | `harden-openspec-authority-and-lifecycle` (ещё не создан) | Запланировать вместе с WL-P1-03. | — | +| WL-P1-05 | Разделить non-mutating lint check и auto-fix backend. | backend | planned | `make-local-verification-agent-safe` (ещё не создан) | Запланировать после P1 lifecycle work. | — | + +### P2 + +| Audit ID | Цель | Репозитории | Статус | Связанный change | Следующее действие | Terminal evidence | +| --- | --- | --- | --- | --- | --- | --- | +| WL-P2-01 | Ограничить размер и eviction operations cache. | backend | planned | `harden-auth-and-cache-concurrency` (ещё не создан) | Уточнить policy cache в design. | — | +| WL-P2-02 | Вернуть предсказуемый 409 при конкурентной регистрации. | backend | planned | `harden-auth-and-cache-concurrency` (ещё не создан) | Добавить parallel integration scenario. | — | +| WL-P2-03 | Сделать refresh rotation атомарной. | backend | planned | `harden-auth-and-cache-concurrency` (ещё не создан) | Выбрать CAS/transaction policy. | — | +| WL-P2-04 | Синхронизировать runtime, OpenAPI и auth success statuses. | backend, frontend | planned | `align-api-contract-and-client-resilience` (ещё не создан) | Выбрать единый status contract. | — | +| WL-P2-05 | Восстановить runnable backend e2e gate. | backend | planned | `make-local-verification-agent-safe` (ещё не создан) | Диагностировать schema-engine failure. | — | +| WL-P2-06 | Добавить проходящий app/test TypeScript gate backend. | backend | planned | `make-local-verification-agent-safe` (ещё не создан) | Разделить app и test compilation. | — | +| WL-P2-07 | Сохранять cached data во время background refetch. | frontend | planned | `align-api-contract-and-client-resilience` (ещё не создан) | Зафиксировать stale-data scenarios. | — | +| WL-P2-08 | Нормализовать cold-start refresh failure. | frontend | planned | `align-api-contract-and-client-resilience` (ещё не создан) | Добавить failure test. | — | +| WL-P2-09 | Проверять backend runtime OpenAPI против frontend snapshot. | backend, frontend | planned | `make-local-verification-agent-safe` (ещё не создан) | Спроектировать read-only contract gate. | — | +| WL-P2-10 | Актуализировать onboarding и постоянную документацию. | docs, frontend, backend, specs | planned | `refresh-project-documentation-and-agent-entrypoints` (ещё не создан) | Сверить docs с delivered features. | — | +| WL-P2-11 | Зафиксировать одинаковый local bootstrap frontend/backend. | frontend, backend, docs | planned | `make-local-verification-agent-safe` (ещё не создан) | Описать Node/npm policy и `npm ci`. | — | +| WL-P2-12 | Проверить применимость high dependency advisories. | frontend, backend | planned | `address-dependency-advisories-and-repo-hygiene` (ещё не создан) | Исследовать call paths до обновления версий. | — | +| WL-P2-13 | Дать одному agent workflow единый local verify interface. | frontend, backend, specs, docs | planned | `make-local-verification-agent-safe` (ещё не создан) | Спроектировать workspace verifier. | — | + +### P3 + +| Audit ID | Цель | Репозитории | Статус | Связанный change | Следующее действие | Terminal evidence | +| --- | --- | --- | --- | --- | --- | --- | +| WL-P3-01 | Сделать unknown request fields явной ошибкой или документированной policy. | backend | planned | `tighten-backend-request-feedback` (ещё не создан) | Выбрать validation policy. | — | +| WL-P3-02 | Устранить shared i18n import cycle. | frontend | planned | `tighten-frontend-quality-guardrails` (ещё не создан) | Добавить cycle rule и leaf import. | — | +| WL-P3-03 | Восстановить FSD public API boundary. | frontend | planned | `tighten-frontend-quality-guardrails` (ещё не создан) | Расширить dependency rule. | — | +| WL-P3-04 | Исправить базовые language/loading accessibility semantics. | frontend | planned | `tighten-frontend-quality-guardrails` (ещё не создан) | Добавить semantic status primitive. | — | +| WL-P3-05 | Ввести пропорциональный bundle regression budget. | frontend | planned | `tighten-frontend-quality-guardrails` (ещё не создан) | Проверить route splitting boundaries. | — | +| WL-P3-06 | Добавить coverage ratchet только как regression signal. | frontend | planned | `tighten-frontend-quality-guardrails` (ещё не создан) | Зафиксировать baseline после risk tests. | — | +| WL-P3-07 | Защитить рабочий `.env` от случайного commit. | frontend | planned | `address-dependency-advisories-and-repo-hygiene` (ещё не создан) | Уточнить `.env`/example policy. | — | +| WL-P3-08 | Исключить runtime SQLite DB из Git. | backend | planned | `address-dependency-advisories-and-repo-hygiene` (ещё не создан) | Проверить безопасный relocation/ignore. | — | + +## История registry + +Terminal evidence добавляется сюда только после завершения соответствующего change. На момент создания registry завершённых audit-derived changes нет. + +### 2026-08-23 — bootstrap verification: `establish-audit-remediation-workflow` + +Этот process change не закрывает Audit ID. Перед archive выполнены следующие read-only checks: + +- `cmp -s audit-20-08-2026.md docs/audits/audit-20-08-2026.md` — exit 0; versioned snapshot byte-for-byte совпадает с исходной локальной копией. +- `openspec validate establish-audit-remediation-workflow --type change --store wealth-ledger-specs` — `Change ... is valid`. +- Структурная проверка registry — 26 records, 26 `planned`, 0 `active`, 0 `done`, одна «Следующая задача»; все четыре agent pointers и оба registry links разрешаются. +- `git diff --check` в frontend, backend, specs и docs — exit 0. +- Независимый read-only review registry-to-audit mapping, OpenSpec links, instruction discoverability и scope diff — `findings: none`. +- `opsx-verify` доступен как OpenCode command, но не установлен как shell executable в этой среде; выполнена эквивалентная процедура из `.opencode/skills/openspec-verify-change/SKILL.md` с теми же dimensions completeness, correctness и coherence. +- Change синхронизирован в main specs и архивирован в [`specs/openspec/changes/archive/2026-08-23-establish-audit-remediation-workflow`](../../specs/openspec/changes/archive/2026-08-23-establish-audit-remediation-workflow/).