Files
codex-bot d07fbfabb1 docs: archive technical audit 2026-10-11 in develop (#36)
Переносит архив `audits/2026-10-11/` (19 файлов) из codex/audit-remediation-2026-10-11 в develop и добавляет точечный allowlist в .gitignore.
2026-10-17 19:38:03 +03:00

766 lines
71 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WealthLedger — Complexity, Redundancy & Engineering Overhead Audit
Дата: 11 октября 2026 года.
Метод: `ponytail-audit`, статический анализ owners, consumers, проверок и последствий удаления.
## Основание и ограничения
Изучены root, `frontend`, `backend`, `docs`, `specs` и обнаруженный отдельный репозиторий `deploy`. Первый отчёт `technical-audit-2026-10-11.md` прочитан. Его функциональные дефекты и уже перечисленные simplification opportunities не включены в новые findings.
Основные checkout совпадают с первым аудитом:
| Репозиторий | Commit |
|---|---|
| frontend | `c882120` |
| backend | `0ad84ff` |
| docs | `e6fd4f6` |
| specs | `fcdc805` |
Выполнены чтение, поиск, Git inspection и подсчёты через `rg`. Единственная дополнительная проверка инструментом:
```sh
source "$HOME/.nvm/nvm.sh"
nvm use --silent
node node_modules/openapi-typescript/bin/cli.js \
src/shared/api/spec/openapi.json \
-o src/shared/api/generated/schema.d.ts \
--check
```
**Результат: exit 0, установленный `openapi-typescript 7.13.0`.** Проверка не записывает output: сравнение и выход происходят до `mkdirSync`/`writeFileSync` — `frontend/node_modules/openapi-typescript/bin/cli.js:125–134,280–291`.
Build, полные verify, тестовые наборы, установки, Docker и deployment не запускались. Финальные `git status --short` всех шести обследованных рабочих деревьев не показали изменений.
Время, стоимость CI, bundle size и расход токенов не измерялись.
# A. Executive Summary
## Главные источники избыточности
1. **Обязательный полный локальный workflow для любого code change.**
Frontend и backend требуют `npm ci` и полный `verify` перед завершением, независимо от изменённой области. Дополнительные conventions отдельно направляют к typecheck/lint/risk checks. Это расширяет небольшую задачу до полного bootstrap и проверки всего приложения.
Evidence: `frontend/.ai/conventions/engineering.md:58–66`, `backend/.ai/conventions/engineering.md:58–66`, `frontend/.ai/conventions/ui.md:165–170`.
2. **Повторная работа внутри одной границы проверки.**
- Frontend `verify` вызывает TypeScript дважды.
- Backend отдельно typecheck’ит application, хотя test typecheck включает те же application sources.
- Backend CI собирает приложение в `verify`, затем повторно в `test:e2e`.
- SQLite runner regression повторяет весь functional suite пять раз ради свойств самого runner.
Evidence: `frontend/package.json:26`, `backend/package.json:22–34`, `backend/tsconfig.typecheck.test.json:7–8`, `backend/.gitea/workflows/verify.yml:84–89`, `backend/test/sqlite-regression.cjs:20–22,77–91`.
3. **Самописная проверка, уже реализованная установленным инструментом.**
`check-openapi.mjs` — 45 строк temp-directory/subprocess/cleanup orchestration для возможности `openapi-typescript --check`. Это наиболее уверенный tooling-cut с сохранением той же гарантии.
4. **Production API, поддерживаемые тестовыми consumers.**
- `useAuthenticatedQueries`: production callers отсутствуют.
- `InstrumentMetadataService.read`: используется metadata persistence suite; production использует `request`/`savedMany`.
Evidence: `frontend/src/shared/api/authenticated-query.ts:15–32,61`, `frontend/src/shared/api/authenticated-query.test.tsx:9–23`, `backend/src/integrations/tbank/instrument-metadata.service.ts:41–63`, `backend/test/tbank-metadata-persistence.e2e-spec.ts:87–382`.
5. **Тесты иногда обходят дешёвую существующую boundary.**
Чистые преобразования проверяются через fake Prisma transactions или background orchestration; page tests поддерживают fake query-result shapes параллельно существующей MSW boundary.
6. **Противоречивая процессная authority.**
Локальные инструкции задают opt-in OpenSpec и запрещают audit artifacts в docs. Действующие process specs требуют registry, snapshots и canonical workflow для audit-derived работы. Это создаёт дополнительные artifacts и решения о том, какую инструкцию исполнять.
Evidence: `specs/AGENTS.md:3–6`, `docs/AGENTS.md:12–14`, `specs/openspec/specs/project-foundation/spec.md:9–19`, `specs/openspec/specs/audit-remediation-workflow/spec.md:9–49`.
## Общая оценка
**Масштабный архитектурный рефакторинг не нужен.** Не подтверждена потребность вводить новые interfaces, generic workers, fixture frameworks, архитектурные validators или quality tools.
Самые полезные изменения сокращают **число путей, запусков и обязательных artifacts**, сохраняя существующие owners и реальные behavioral proofs.
# B. Complexity Inventory
## B1. Количественные показатели
| Метрика | Результат | Метод и ограничения |
|---|---:|---|
| Package script entries | **67** | Root 3; frontend 25; backend 27; docs 5; specs 5; UI/icons workspace builds 2. Это entrypoints и aliases, не 67 самописных программ |
| Custom script/adapter/harness/plugin files | **21** | Frontend 6; backend 10; docs 2; specs 1; deploy 2. Git hooks и декларативные configs отдельно |
| Custom Git hooks | **3** | Frontend pre-commit/commit-msg; specs commit-msg. Generated Husky files и Git samples исключены |
| Custom release hook declarations | **4** | Frontend/backend/docs/specs `after:git:init`; root такого helper не имеет |
| Frontend `verify` command stages | **12** | Разделение команды на последовательные стадии; вложенные workspace builds отдельно не разворачиваются |
| Backend `verify` command stages | **6** | Lint, lint-config tests, два typecheck, unit coverage, build |
| Operational verification families | **29** | Группы в разделе C; не количество отдельных ESLint rules или test assertions |
| Участки повторной/малоинформативной проверки | **7** | Frontend TS; backend TS subset; backend rebuild; full-suite runner repetition; preset-parity meta-tests; docs/specs clean-checkout diff checks |
| Test files frontend/backend | **63 / 59** | Backend: 39 colocated specs + 20 integration suites. Browser files входят в frontend 63 |
| Test files вместе с deploy harness | **123** | 122 TS/TSX test files + `deploy/tests/deploy.sh`; CJS инфраструктурные проверки здесь отдельно |
| Production TS/TSX | **34 465 строк** | Frontend 16 967; backend 17 498. Generated, stories, fixtures/test support исключены |
| Test TS/TSX | **23 679 строк** | Frontend 11 537; backend 12 142 |
| Test/production ratio | **≈ 0,69** | Физические строки, включая comments/blank lines. Не критерий качества архитектуры |
| Frontend stories / support | **18 файлов / 6 файлов** | 1 855 / 263 строки соответственно; не включены в test LOC |
| Классифицированные factory/polling definitions | **не менее 18** | Frontend 3 общие factories; backend 4 service factories, 4 deferred helpers, 7 polling helpers. Локальные render/helpers не полностью включены |
| Frontend module mocks | **4 в 3 файлах** | `vi.mock`; не учитывает spies и MSW handlers |
| Backend module mocks | **18 в 12 unit files** | `jest.mock`; сам count не является finding |
| Snapshot assertions | **0 найдено** | Поиск `toMatchSnapshot`, `toMatchInlineSnapshot`, `toMatchFileSnapshot`, `__snapshots__` в обследованных test sources |
| Backend runtime SHA/HMAC mechanisms | **8** | 7 `createHash` emission sites + один signed-cursor mechanism с encode/decode HMAC. Password bcrypt отдельно |
| Gitleaks archive checksum steps | **4** | Одна download-integrity policy в четырёх независимых repos |
| OCI digest publication paths | **2** | Frontend/backend image workflows |
| Подтверждённые точки удаления | **18** | Перечень в G; считаются удаляемые элементы/стадии, не файлы |
| Обследованные single-production-consumer abstractions | **6 примеров** | RouteTransition, InstrumentSearch, useInstrumentSearch, metadata repository `work`, `queueSnapshot`, `TBANK_ACCESS`. Это выборка, не полный census |
LOC и количества используются для описания масштаба. Они **не доказывают** избыточность.
## B2. Все custom scripts и исполняемые adapters
| Файл | Назначение и consumers; автоматизация | Пересечение / последствия удаления / решение |
|---|---|---|
| `frontend/scripts/check-openapi.mjs:1–45` | Freshness generated types; вызывается `openapi:check` и `verify` | Полностью покрывается native `--check`. **Удалить реализацию, сохранить проверку** |
| `frontend/scripts/check-release.cjs:1–24` | Manifest version ↔ reachable stable tag; release-it hook | Не эквивалентен `requireCommits`. **KEEP**: предотвращает повторную подготовку версии |
| `frontend/scripts/test-eslint.mjs:23–395` | Config/rule/fixer proofs; автоматически из `test:lint`/`verify` | Смешивает проектные checks и exhaustive preset parity. **SIMPLIFY**, сохранять probes реальных exceptions/custom fixers |
| `frontend/scripts/test-pre-commit.mjs:17–79` | Staged-index scanning, redaction, fail-closed; security CI и ручная команда | Full-history scan не доказывает index behavior. **KEEP** |
| `frontend/scripts/check-image-publication.cjs:15–88` | Модель retry после post-push failure, exact digest/revision/smoke | Автоматических callers не найдено. Не считать dead только из-за этого: самостоятельный workflow proof. **KEEP как targeted check**, не обязательный gate каждой правки |
| `frontend/eslint-rules.mjs:1–178` | Два custom ESLint rules; загружается lint config | Часть правил — formatting/обязательная форма кода. **SIMPLIFY**, не заменять новым analyzer |
| `backend/scripts/check-release.cjs:1–11` | Version/tag guard; release-it hook | Штатный release guard не покрывает эту policy. **KEEP** |
| `backend/scripts/generate-openapi.cjs:1–41` | Swagger generation из built app без deployment config | Package command и contract integration suite. TypeScript сам контракт не генерирует. **KEEP** |
| `backend/test/sqlite.cjs:1–95` | Per-suite temporary DB, migrations, subprocesses, signal cleanup | Все SQLite entrypoints. Обычный Jest не гарантирует DB isolation. **KEEP** |
| `backend/test/sqlite-regression.cjs:1–96` | Runner isolation/failure/signal proof; ручной README entrypoint | Пять полных успешных suite runs избыточны для runner properties. **SIMPLIFY область прогонов** |
| `backend/test/e2e-env.cjs:1–17` | Отказ от не-изолированной DB, synthetic credentials | Jest setup и crash fixture. **KEEP**: защита данных developer |
| `backend/test/fixtures/tbank-sync-crash.cjs:1–86` | Управляемый kill процесса на durable boundaries | Consumer — operations persistence suite. In-process mocks не заменяют crash proof. **KEEP** |
| `backend/test/eslint-config.cjs:9–146` | Profile scope, generated ignores, import exceptions | Автоматически `test:lint`. **SIMPLIFY**: project scope/exception checks полезны; поведение каждой library rule не нужно доказывать заново |
| `backend/test/runtime-check.cjs:1–8` | Проверяет строку start command, наличие dist и require generated client | Runtime callers не найдены; config test использует имя как fixture path. Real image smoke сильнее. **DELETE** |
| `backend/test/container-smoke.cjs:1–77` | Native modules/assets, migration, non-root/read-only runtime, auth/restart | Image publication и manual package entrypoint. Host build это не доказывает. **KEEP** |
| `backend/test/bank-cards-durability.cjs:1–219` | Historical build → migration → restart → rollback → restore | Risk-triggered manual entrypoint. Fresh DB suites не заменяют historical compatibility. **KEEP** |
| `docs/scripts/check-release.cjs:1–10` | Clean status + version/tag guard | Release hook. Porcelain видит untracked files; native clean guard не полностью эквивалентен. **KEEP** |
| `docs/.markdownlint-rule-relative-links.cjs:1` | Adapter `.default` установленного link rule | Оба markdownlint запуска. Не собственный validator. **KEEP** |
| `specs/scripts/check-release.cjs:1–10` | Clean status + version/tag guard | Release hook. **KEEP** |
| `deploy/deploy.sh:1–192` | Tagged snapshot, preflight, lock/pending, backup, migration/readiness | Ручной запуск, затем self-handoff к tagged script. **KEEP** |
| `deploy/tests/deploy.sh:1–251` | Real Git/files/locks + Docker double; optional bind-restore proof | Ручной entrypoint. **KEEP**: orchestration и bind semantics не доказываются application tests |
**Не установлена необходимость централизовать десятистрочные release helpers между независимыми repos.** Общий tooling package добавит dependency boundary и release ownership ради небольшого копирования.
## B3. Package scripts: роль и оправданность
Определения: `package.json:8–12`, `frontend/package.json:9–34`, `backend/package.json:11–38`, `docs/package.json:8–14`, `specs/package.json:8–14`.
| Группа entrypoints | Consumers и оценка |
|---|---|
| Root `release:preview`, `release:prepare`, `commitlint` | Release/operator и commits CI. Штатные CLI wrappers; оставить |
| Frontend `dev`, `dev:catalog`, `build:catalog`, `preview:catalog`, `preview` | Developer/catalog lifecycle. Разные outputs; не дублируют одну проверку |
| Frontend `build`, `build:packages`, `typecheck` | Application/workspace compilation. Packages нужны из-за dist-based exports; проблема — частота повторного запуска |
| Frontend `lint`, `lint:eslint`, `lint:fix` | Alias, check, explicit edit. `lint` alias сам по себе не требует удаления |
| Frontend `test`, `test:run`, `test:coverage`, `test:lint` | Watch, one-shot, coverage, tooling tests. Различимые режимы; уменьшать обязательность, не механически удалять entrypoints |
| Frontend `openapi:generate`, `openapi:check` | Explicit generation и read-only freshness. Оставить оба; check реализовать native CLI |
| Frontend `knip`, `deps`, `deps:storybook` | Dead usage, production boundaries, transitive catalog isolation — разные свойства |
| Frontend `verify`, `prepare`, release pair, `commitlint` | Aggregate gate, Husky bootstrap, release и commit policy. `verify` упростить |
| Backend `start`, `start:dev`, `start:debug`, `start:prod` | Различные runtime/debug modes; production также задаётся Docker |
| Backend `build`, `openapi:generate` | Prisma/Nest output и public artifact; оставить standalone completeness |
| Backend `format`, `lint`, `lint:check`, `lint:fix` | Aliases и явный check/edit. Небольшая стоимость, нет причины строить новые wrappers |
| Backend `typecheck:app`, `typecheck:test` | Оставить targeted entrypoints; в полном gate достаточно test superset |
| Backend `test`, `test:watch`, `test:cov`, `test:debug` | Runner modes; текущая необходимость debug не опровергнута отсутствием CI caller |
| Backend `test:e2e`, `test:sqlite`, `test:performance`, `test:openapi-contract` | Full functional, auth/cards subset, отдельный benchmark, отдельный contract proof. Сокращать repeated build при orchestration, не смешивать наборы |
| Backend `test:container`, `test:durability` | Image/historical-data boundaries; risk-triggered |
| Backend `verify`, `test:lint`, release pair, `commitlint` | Aggregate/tooling/release policy; упростить aggregate |
| Docs `lint`, `verify`, release pair, `commitlint` | Содержательный Markdown gate. Два lint запуска обрабатывают разные наборы с разными changelog rules |
| Specs `verify`, release pair, `commitlint`, `prepare` | `verify` полезен только как local diff check в нынешнем виде; `prepare` незаметно меняет hooksPath |
| UI/icons `build` | Vite bundle + TypeScript declarations для двух реально используемых packages |
## B4. Hash, SHA, freshness и integrity
| Группа / механизм | Реальный failure mode | Альтернативные защиты и риск удаления | Решение |
|---|---|---|---|
| **Business idempotency:** bank intent fingerprint | Повтор запроса после lost response; тот же ID с другим содержимым | Unique ID предотвращает вторую запись, но не различает повтор и конфликтующий intent. `backend/src/modules/banking/bank-cards/application/bank-card-input.ts:74–81`, `bank-cards.service.ts:76–81`, `bank-operations.service.ts:216–223` | **KEEP** |
| **Security:** refresh-token SHA и password bcrypt | Чтение БД не должно раскрывать reusable token/password; rotation CAS | CAS не заменяет безопасное хранение. `backend/src/modules/auth/application/auth.service.ts:52,110–133,180,204–205` | **KEEP** |
| **Cursor integrity:** HMAC + revision/filter binding | Подмена cursor или применение к изменённому dataset/filter | Ownership проверяет доступ, но не подписанность и согласованность cursor. `backend/src/modules/investments/operations/application/brokerage-operations.service.ts:347–388`, `backend/src/modules/investments/operations/infrastructure/brokerage-operations.repository.ts:128–143` | **KEEP** |
| **Work identity:** hash canonical operations intervals | Разные диапазоны не должны случайно объединяться; одинаковые intents coalesce | Возможна строковая canonical identity, но существующий механизм короткий и связан с durable keys. `backend/src/modules/investments/operations/infrastructure/brokerage-operations.repository.ts:254–269` | **KEEP**, нет доказанного выигрыша от замены |
| **Resource identity/revisions:** accounts, candles, operations | Согласованность metadata и identity текущей read-model | Это public/runtime metadata, не development gate. `backend/src/modules/investments/brokerage-accounts/brokerage-accounts.service.ts:42–54`, `backend/src/modules/investments/instruments/infrastructure/candle.repository.ts:41–52`, `backend/src/modules/investments/operations/application/brokerage-operations.service.ts:238–239` | **KEEP до установленного contract-safe cut** |
| **Freshness metadata/TTL** | Отсутствие данных, stale и confirmed completeness нельзя смешивать | Coverage/revisions защищают другие свойства. `backend/src/modules/investments/investment-resource.mapper.ts:31–64,78–88`, `backend/src/integrations/tbank/tbank-source.policy.ts:1–21` | **KEEP**; удалить только неиспользуемый `candleTail` key |
| **Data integrity:** SQLite integrity/FK checks | Повреждение/невалидные связи после migration/restore | Unit mocks и successful startup недостаточны. `backend/test/bank-cards-durability.cjs:109–118,190–205`, `backend/test/container-smoke.cjs:58–64` | **KEEP на соответствующих boundaries** |
| **Generated artifacts:** local OpenAPI → types | Committed types расходятся с предоставленным snapshot | TS не сравнивает types с JSON. Native `--check` покрывает custom script | **SIMPLIFY implementation, KEEP guarantee** |
| **Runtime OpenAPI generator parity** | Generator отличается от configured app schema | Frontend freshness этого не доказывает. `backend/test/openapi-contract.e2e-spec.ts:108–156` | **KEEP** |
| **Download integrity:** Gitleaks tarball SHA-256 | Исполнение не тех bytes скачанного binary | HTTPS/version URL не эквивалентны pinned checksum. Frontend/backend verify `52–58`, docs/specs security `24–30` | **KEEP** |
| **Image content identity:** OCI digest/revision | Smoke проверяет другой image, tag изменён, retry скрывает несовпадение | Host tests не доказывают bytes опубликованного image. `frontend/.gitea/workflows/image.yml:46–62`, `backend/.gitea/workflows/image.yml:42–55` | **KEEP** |
| **Process identity:** release manifest/tag, deployment Git commit | Повторная подготовка версии; deployment не из выбранного tagged snapshot | Native release-it покрывает лишь часть; рабочий checkout не immutable input. `deploy/deploy.sh:26–39` | **KEEP** |
| **Process visual SHA tables** | Approved export изменён отдельно от approval input | Git commit binding может покрывать Git-only delivery; отдельно передаваемые exports — другой риск. `specs/openspec/schemas/wealth-ledger-sdd-v2/schema.yaml:322–330`, `specs/openspec/schemas/wealth-ledger-sdd-v2/templates/design.md:38–61` | **Условный SIMPLIFY**, не безусловное удаление |
| **npm lockfile integrity** | Package bytes не соответствуют lockfile | Стандартный механизм npm, не custom framework | **KEEP** |
**Самописных self-hash/fingerprint validators для planning Markdown или hash-gated reuse CI результатов не обнаружено.** Требования в prose не равны действующей автоматизации.
# C. Redundancy Matrix
## Фактическая цепочка
### Frontend
```text
Developer / AI:
npm ci
targeted typecheck/lint/tests
full verify
pre-commit:
staged Gitleaks → lint-staged ESLint → deps → build:packages
CI:
commitlint → history Gitleaks / audit / hook proof
→ full verify
full verify:
packages → OpenAPI freshness → TS → ESLint → lint-config tests
→ Vitest coverage → Knip → production graph → story graph
→ TS again → application build → Storybook build
```
Источники: `frontend/.ai/conventions/engineering.md:58–66`, `frontend/.husky/pre-commit:1–3`, `frontend/.gitea/workflows/verify.yml:17–100`, `frontend/package.json:26`.
### Backend
```text
Developer / AI:
npm ci → targeted checks → full verify → risk-specific suites
CI:
commitlint → history Gitleaks / audits
→ Prisma generate → verify
→ test:e2e → build again → isolated SQLite suites
```
Источники: `backend/.ai/conventions/engineering.md:58–66`, `backend/.ai/conventions/testing.md:38–57`, `backend/.gitea/workflows/verify.yml:84–89`, `backend/package.json:28–34`.
### Docs / specs / deploy
- Docs: Markdown/link lint + local diff check; CI content/security/commit checks.
- Specs: local `git diff --check`; CI устанавливает Node dependencies и повторяет эту команду на чистом checkout. Native OpenSpec validation документирована отдельно, но не вызывается этим job.
- Deploy: отдельный ручной lifecycle и shell harness, вне application CI.
## Матрица
Частота ниже — **сценарная**, не измеренная: один local final verify, один commit и один CI event. Для checks outside этой цепочки указан их фактический trigger.
| Mechanism | Purpose / consumers | Overlap и частота | Cost | Recommendation | Confidence |
|---|---|---|---|---|---|
| 1. TypeScript | Source/tooling diagnostics; local/CI | FE: 4 calls в двух verify. BE: по 2 app/test calls; test включает app | Повтор compiler invocation; incremental может удешевлять | Убрать FE второй call; BE app из aggregate | High |
| 2. ESLint | Syntax, async, security/style policies | FE: 2 full + staged hook; scopes различаются | Typed lint всего проекта | KEEP full CI; local scoped; staged сохранить | High |
| 3. Lint-config tests | Scope/exceptions/custom fixers | 2 full executions; часть заново доказывает presets | Synthetic lintText/config calculations | Сократить generic parity; project probes по tooling trigger | High |
| 4. Knip | Dead exports/dependencies/entries | 2 executions; TS не эквивалентен | Whole-project analysis | KEEP CI; local по соответствующему изменению | High |
| 5. Production dependency graph | FSD/package/test boundaries, cycles | FE: local + hook + CI = 3 | Hook дополнительно builds packages | Перенести из каждого commit в scoped local/CI | High |
| 6. Story reachable graph | Catalog не достигает application data | 2 executions; base config включён, но graph semantics отличаются | Второй graph traversal | KEEP: не объединять вслепую | High |
| 7. Generated OpenAPI freshness | Snapshot ↔ types bytes | 2 executions; TS не заменяет | Custom temp/subprocess machinery | Native `--check` | High |
| 8. Unit/RTL/MSW tests | Own behavior, transport/UI results | Обычно 2 full runs; local/CI разные trust boundaries | Whole suite local | Targeted local, full CI | High |
| 9. Browser layout | Реальная геометрия narrow/wide | Два viewport instances на run | Chromium/runtime setup | KEEP для geometry changes и CI | High |
| 10. Coverage | FE floor; BE report | По одному сбору на full test run | Instrumentation/reporting | CI report; local targeted без coverage | High |
| 11. Application build | Emission/bundling/assets | BE CI два builds подряд; Docker — отдельная boundary | Prisma/Nest/Vite work | Убрать same-job rebuild; Docker оставить | High |
| 12. Storybook build | Catalog bundle | Local/CI по одному; app build не заменяет | Отдельный bundle | CI/catalog-risk scope | High |
| 13. SQLite HTTP/integration | DB constraints, guards, contract | CI 19 functional suites; standalone 20-я performance отдельно | Per-suite migration + Jest | KEEP independent DB isolation | High |
| 14. Runner regression | Isolation, cleanup, signals | Пять успешных `--all` + failure/signal paths вручную | 95 successful suite executions | Representative two-suite runs | High facts / Medium cut |
| 15. Historical durability | Migration/rollback/restore | Schema/data-risk trigger | Separate historical build/app runs | KEEP risk-triggered | High |
| 16. Image smoke | Actual image runtime/assets/persistence | Publication/manual; host checks не эквивалентны | Docker lifecycle | KEEP | High |
| 17. Publication model test | Retry/digest failure branches | Только explicit invocation найден | Stateful command double, YAML block extraction | KEEP focused, не every-change gate | Medium |
| 18. Staged secret scan | Не допустить secret в commit | Каждый FE commit | Index scan | KEEP; history scan позднее | High |
| 19. History secret scan | Secrets во всей Git history | PR/push/schedule | Full-history scan/download | KEEP; оптимизировать только по измерениям | High |
| 20. Hook security proof | Index vs worktree, redaction/fail-closed | FE security CI | Temp Git repo/real scanner | KEEP; разные гарантии с №19 | High |
| 21. Dependency audit | Known advisories | CI; BE runtime/dev thresholds различаются | Network/advisory processing | KEEP; overlap не доказан одинаковыми properties | High |
| 22. Commitlint | PR title и retained commits | CI 2 invocations; local commit hook дополнительно | App repos устанавливают полный manifest для job | KEEP policy; не добавлять custom parser | High |
| 23. Release preparation guard | Branch/tree/version lifecycle | Explicit release prepare | Несколько Git queries | KEEP; preview не считать proof custom hook | High |
| 24. Image version/digest binding | Tag/package/lock/revision/published bytes | Release-tag workflow | Metadata/registry checks | KEEP release boundary | High |
| 25. Scanner checksum | Downloaded scanner bytes | 4 repo-local security paths | Один hash command | KEEP | High |
| 26. Markdown/link checks | Docs syntax/local targets | 2 disjoint lint commands/run | 50 Markdown files | KEEP; не второй custom link checker | High |
| 27. `git diff --check` | Working diff whitespace/conflict markers | Local useful; docs/specs CI clean checkout малоинформативен | Specs CI ещё устанавливает deps | KEEP local; удалить пустой CI work | High |
| 28. Native OpenSpec validation | Schema/change/spec structure | Manual; v2 instructions требуют после 7 stages | Repeated CLI/context work | Только explicitly selected SDD; proportional scope | High facts / Medium policy |
| 29. Deploy preflight/lifecycle tests | Lock, backup, ordering, failure cleanup | Manual deploy/harness | Docker doubles/real locks/files | KEEP | High |
### Важные разграничения
- **Local и CI не объявлены дубликатами только потому, что запускают одинаковую команду.** Сокращается обязательный local scope, CI остаётся независимой проверкой.
- Production и Storybook dependency-cruiser отличаются `tsPreCompilationDeps: true/false`: `frontend/dependency-cruiser.cjs:78–87`, `frontend/.storybook/dependency-cruiser.cjs:22`. Один простой запуск не доказан эквивалентным двум.
- OpenAPI generator parity и consumer type freshness проверяют разные отношения.
- Coverage, browser layout и JSDOM behavior не взаимозаменяемы.
# D. SOLID / DRY / KISS / YAGNI Findings
## SRP
### D1. Test-only orchestration внутри production metadata service
`InstrumentMetadataService.read` совмещает access, saved lookup, demand start и work lookup, но production callers используют отдельные existing methods.
- Owner: `backend/src/integrations/tbank/instrument-metadata.service.ts:41–63`.
- Production примеры: `backend/src/modules/investments/portfolio/application/portfolio.service.ts:122–123,275–283,309–315`, `backend/src/modules/investments/operations/application/brokerage-operations.service.ts:213–216`, `backend/src/modules/investments/upcoming-events/upcoming-events.service.ts:200–206`.
- Test consumer: `backend/test/tbank-metadata-persistence.e2e-spec.ts:87–382`, 38 call sites.
**Вывод:** убрать совмещённый API после перевода тестов на production-used boundary. Не удалять persistence suite и её concurrency/restart proofs.
### D2. Portfolio имеет ложную DI-зависимость и два имени одной операции
- Неиспользуемый `TbankMembershipService` constructor dependency: `backend/src/modules/investments/portfolio/application/portfolio.service.ts:9–18,45–53`.
- Public `request` → private `queueSnapshot` без дополнительной policy: тот же файл, `234–243`.
- Тест обходит public operation и читает private repository: `backend/test/tbank-value-history.e2e-spec.ts:99–111,247–256,307–313`.
**Вывод:** один public `request`, без unused DI; тест получает существующий repository provider напрямую. Не дробить queue/publication lifecycle.
## OCP / YAGNI
### D3. Plural authenticated-query boundary существует ради теста
`frontend/src/shared/api/authenticated-query.ts:15–32,61`; единственный consumer — `frontend/src/shared/api/authenticated-query.test.tsx:9–23`.
Тестовая plural branch содержит один query и берёт `[0]`. Приложение потребляет singular hook.
**Вывод:** удалить plural implementation и вариант test matrix. Singular auth gating оставить.
### D4. InstrumentSearch сохраняет stock-вариант без product consumer
- Union contract: `frontend/src/features/instrument-search/ui/InstrumentSearch/InstrumentSearch.view.tsx:18–31`.
- Единственный production renderer consumer — BondsPage: `frontend/src/pages/bonds/ui/BondsPage/BondsPage.view.tsx:26–113`.
- Stock-конфигурация используется тестом: `frontend/src/features/instrument-search/ui/InstrumentSearch/InstrumentSearch.test.tsx:21–29`.
**Вывод:** специализировать current renderer под bond, сохранив debounce, URL/search states и presentation cases. Не объединять bond search со stock screener.
### D5. Малые невостребованные элементы поверхности
| Элемент | Evidence | Минимальное действие |
|---|---|---|
| `history.retry` | `frontend/src/entities/broker-operation/model/useBrokerageOperationHistory.ts:52–61,76`; все три VMs его не читают | Удалить action; automatic invalid-cursor recovery `37–50` оставить |
| `isFetching` input | `frontend/src/shared/lib/initial-availability.ts:10–16,18–46`; не читается | Удалить из type, пяти callers и fixtures |
| `membershipWork()` | `backend/src/integrations/tbank/tbank-access.service.ts:90–97`; callers не найдены | Удалить метод, не одноимённое repository result field |
| `candleTail` TTL key | `backend/src/integrations/tbank/tbank-source.policy.ts:3–8`; production references отсутствуют | Удалить key; настоящую candle coverage/tail policy оставить |
| `HourlySyncSectionDto` | `backend/src/modules/investments/tbank-sync/tbank-sync.dto.ts:232–250`; current problems DTO другой, `271–272` | Удалить непривязанный class |
| Export `totalsByCurrency` | `backend/src/modules/investments/portfolio/infrastructure/tbank-portfolio.mapper.ts:56,109–141`; используется внутри файла | Удалить export, не calculation |
## ISP / DIP
### D6. String DI alias для единственной реализации
- Structural contract/string token: `backend/src/modules/investments/instruments/stocks/application/stock-screener.service.ts:49–63`.
- Единственный `useExisting` alias: `backend/src/modules/investments/instruments/instruments.module.ts:23–25`.
- Alternative implementations/overrides token не найдены.
**Вывод:** injection по существующему concrete class token. Узкий type можно сохранить через `Pick`, как уже сделано в `backend/src/modules/investments/tbank-connection/tbank-connection.service.ts:11–19`.
Это небольшой cleanup, не повод создавать interface.
### D7. FormProvider без context consumers
`frontend/src/features/auth/ui/AuthForm/AuthForm.tsx:50–86`: поля используют локальный `methods.register`, descendants не используют form context.
Но `frontend/.ai/conventions/ui.md:128–129` предписывает `FormProvider`.
**Вывод:** сначала сделать existing rule условным по наличию context consumers, затем убрать provider здесь. Не создавать consumers ради оправдания wrapper.
## LSP
Для основной business architecture **практически неприменим**: нет собственной иерархии взаимозаменяемых domain implementations. Concrete providers/repositories — не нарушение LSP или DIP.
DTO inheritance может сохранять Swagger metadata/schema identity — например `backend/src/modules/investments/instruments/bonds/presentation/bond.dto.ts:5–16`. Удалять пустые DTO subclasses по визуальному сходству нельзя.
## DRY
Подтверждены узкие дубли knowledge/assertions:
- Anonymous `/accounts` redirect повторён table case и standalone case: `frontend/src/app/routes/AppRoutes/AppRoutes.test.tsx:35–61`.
- Failed-image behavior повторяется на page level, хотя сильнее проверен EntryCard: `frontend/src/pages/account-events/ui/AccountEventsPage/AccountEventsPage.test.tsx:166–169`, `frontend/packages/ui/src/EntryCard/EntryCard.test.tsx:24–63`.
- Process authority повторяет approval/cascade/context policies в canonical specs, schemas и config: `specs/openspec/config.yaml:55–58`, `specs/openspec/specs/openspec-authority-and-lifecycle/spec.md:53–80`, `specs/openspec/schemas/wealth-ledger-sdd-v2/schema.yaml:554–566,625–642`.
Не рекомендовано объединять:
- короткие local money fixtures;
- worker implementations с разной publication semantics;
- repo-local release configs;
- похожие UI/domain states с разными responsibilities.
## KISS
### D8. Motion dependency ради одного opacity transition
- Единственный source consumer: `frontend/src/app/routes/RouteTransition.tsx:3–30`.
- Единственный caller: `frontend/src/app/routes/RouteFallback.tsx:70–81`.
- Dependency: `frontend/package.json:52`.
**Минимум:** локальный CSS fade-in с `prefers-reduced-motion`; сохранить `key={pathname}` и current mount boundary.
Потенциально удаляется **одна runtime dependency**. Bundle savings не измерены. Это replacement opportunity, а не unused package в текущем коде.
# E. Test Suite Complexity
## E1. Setup architecture
### Frontend
Общая инфраструктура небольшая:
- `frontend/src/test/query.tsx:5–24`: две Query factories, consumers — 7 и 20 test files.
- `frontend/src/test/resource.ts:6–25`: одна resource-envelope factory, 20 test consumers.
- `frontend/src/test/setup.ts:13–48`: MSW lifecycle, RTL cleanup, auth reset, browser substitutes.
- `frontend/src/test/server.ts:1–7`: прямой MSW server.
- `frontend/src/test/storybook.test.tsx:19–57`: isolation checks для девяти story modules.
**Общего самописного test framework не обнаружено.** Размножение локальных render/provider setups не требует автоматически создавать универсальный render helper.
### Backend
- 20 integration suites: 14 AppModule, 5 direct repository/service boundaries, одна migration suite.
- 14 Nest bootstrap sites, 23 overrideProvider call sites.
- 4 service factories, 4 deferred definitions, 7 polling helpers.
- 8 CJS инфраструктурных/helper файлов, перечисленных в B2.
Fresh DB на suite, synthetic credentials и signal cleanup — необходимая стоимость. Не рекомендовано объединять все suites в общую DB ради меньшего setup.
## E2. Участки для сокращения
В таблице ниже сокращённые frontend filenames принадлежат указанным page/component directories; backend filenames — `src/modules` или `test` по полным путям в разделах D и H.
| Набор | Что защищает | Лишняя подготовка / overlap | Что теряется и решение |
|---|---|---|---|
| `frontend/src/pages/account-events/ui/AccountEventsPage/AccountEventsPage.test.tsx:13–28,40–61,83–205` | Route scope, loading/busy, events/empty/error presentation | Два module mocks и пять fake query shapes через `as never`; соседняя `AccountEventsPage.filters.test.tsx:50–108` уже использует MSW | **Упростить**, не удалять outcomes. Network cases — existing MSW; presentation-only — existing view |
| `backend/src/modules/banking/bank-cards/application/get-bank-summary.use-case.spec.ts:11–48` | Category mapping, точное `1.23`, inconsistent category rejection | Fake Prisma tx + настоящий repository, хотя use case вызывает только `summaryData()` | Mock ближайшую repository boundary. DB proof не теряется: fake tx его не обеспечивал |
| `backend/src/modules/investments/portfolio/application/portfolio.service.spec.ts:48–165` | Signed nanos, unknown type, cash/quantity/currency mapping | Background request, polling и `publish.mock.calls` для чистого mapper result | Первые два cases напрямую через existing `mapPortfolio`; orchestration/integration proof оставить |
| `backend/src/modules/investments/operations/presentation/brokerage-operations.controller.spec.ts:1–18` | Передача args в mock service | Prisma mock, casts, assertion прямого вызова; configured HTTP cases покрывают account/query/access | **Удалить**. HTTP proof: `backend/test/tbank-ledger-local.e2e-spec.ts:113–137,194–227,336–366`, `backend/test/all-accounts.e2e-spec.ts:63–104` |
| `frontend/src/app/routes/AppRoutes/AppRoutes.test.tsx:55–61` | Anonymous redirect | Более сильный table case уже на `35–53` | **Удалить standalone case** |
| `frontend/src/pages/account-events/ui/AccountEventsPage/AccountEventsPage.test.tsx:166–169` | Image error fallback | EntryCard test проверяет также same-source rerender/new-source reset | Удалить только error exercise; event→logo/initial assertions оставить |
| `backend/test/tbank-metadata-persistence.e2e-spec.ts:87–382` | Reuse, coalescing, privacy, late publication, reopen, batch lookup | Сценарии завязаны на production-unused `read()` | **Перевести boundary**, не удалить suite |
| `backend/test/bank-cards.e2e-spec.ts:371–389,710–753` | Rename/default role, durability, FK/unique | Три declarations зависят от предыдущих tests и их category state | Сделать три cases самодостаточными существующим `createCard`; не fixture framework |
| `backend/test/sqlite-regression.cjs:20–22,77–91` | Runner isolation/cleanup/signals | Пять повторов всех 19 functional suites | По две representative suites/run; canonical full functional CI оставить |
| `frontend/scripts/test-eslint.mjs:148–202` | Все recommended plugin rules включены | Exhaustive parity каждой library preset | Удалить blanket parity; оставить project exceptions/scopes/custom behavior probes |
## E3. Assertions против стоимости setup
Наиболее явные несоразмерности:
- **18 строк controller unit** поддерживают исключительно mock delegation.
- **Две mock layers + пять query shapes** конкурируют с уже существующей HTTP boundary в одном page area.
- **Пять полных SQLite runs** защищают свойства runner, не пять разных domain scenarios.
- **Fake Prisma transaction** требуется для двух assertions чистого summary use case.
Большие financial fixtures или длинный crash setup сами по себе не плохи: они могут защищать boundary, недоступную дешёвому test level.
## E4. Что оставить
- Денежные unit cases: signed nanos, значения выше `2^53`, currencies, unknown≠zero.
- Real SQLite CAS/transactions/replay/refund constraints.
- Session transport/generation fencing.
- Metadata/candle/operations late-publication и restart/crash cases.
- Configured HTTP guards/pipes/cookie/status tests.
- Browser geometry.
- Historical migration/rollback/restore.
- Story isolation, когда она доказывает отсутствие application network/storage access.
Примеры: `backend/src/modules/banking/bank-cards/domain/bank-card.calculations.spec.ts:46–159`, `backend/test/bank-cards.e2e-spec.ts:462–523,622–683`, `backend/test/app.e2e-spec.ts:161–213`, `backend/test/tbank-operations-persistence.e2e-spec.ts:74–100`.
**Не установлено, какие tests были созданы исключительно ради coverage.** Frontend floors (`frontend/vitest.config.ts:19–24`) создают constraint, но не доказывают происхождение тестов. Snapshot bloat не найден.
# F. AI Agent & Developer Workflow
## F1. Bootstrap и full gate обязательны слишком широко
Одинаковое требование в обоих application repos:
> Code/dependency change: `npm ci`, полный `npm run verify`…
`frontend/.ai/conventions/engineering.md:60–62`, `backend/.ai/conventions/engineering.md:60–62`.
Для code-only изменения в уже подготовленном checkout повторная установка не устанавливает новый инвариант. Full frontend verify также запускает browser tests, Knip, оба dependency graphs и Storybook build.
**Упростить:** bootstrap при новом checkout/изменении lockfile или toolchain; local checks по affected behavior; полный независимый CI gate сохранить.
Потеря — более поздний feedback по незатронутым областям, а не отсутствие merge-time проверки.
## F2. Pre-commit собирает packages и проверяет весь graph на каждом commit
`frontend/.husky/pre-commit:1–3`, `frontend/package.json:25,33`.
Даже Markdown commit достигает `npm run deps`, который сначала builds workspaces. UI/icons builds включают Vite и declaration compilation: `frontend/packages/ui/package.json:23–25`, `frontend/packages/icons/package.json:19–21`.
**Перенести graph check из hook в scoped local/CI.** Staged secret scan и staged ESLint оставить.
## F3. Mechanical lint policy создаёт искусственную форму кода
- Вложенные component helpers запрещены custom rule: `frontend/eslint-rules.mjs:63–85`.
- Локальные Zod declarations/refinements требуют filename suffix: тот же файл, `20–36`.
- JSX sibling spacing имеет собственный AST traversal/fixer: тот же файл, `104–175`.
- Любой inline JSX callback/conditional prop запрещён: `frontend/eslint.config.mjs:273–283`.
- Backend magic-number profile требует имена даже для `0`, `1` и bigint literals: `backend/test/eslint-config.cjs:78–92`.
Часть этих правил защищает readability preferences, а не data flow или runtime correctness. Они увеличивают число именованных declarations, файлов и exceptions.
**Упростить blanket restrictions**, оставив semantic boundaries: no data fetching во view, безопасные promises, stable query client, доступность, named business policy values. Не заменять custom policy ещё одним инструментом.
## F4. Generic skills конфликтуют с локальным соразмерным workflow
- `writing-plans` автоматически применим к multi-step requirements: `.agents/skills/workflow/writing-plans/SKILL.md:3`.
- Предписывает файл в `docs/superpowers/plans`: тот же файл, `16–17`.
- Добавляет fixed test/commit structure и Review Focus cases: тот же файл, `43–50,77–87,108–137`.
- TDD применяется к refactoring и требует failing test до любого production code: `.agents/skills/workflow/test-driven-development/SKILL.md:18–45`.
- Локальный Test Intent не требует отдельного artifact, исключает trivial wrapper/declaration testing: `frontend/.ai/conventions/engineering.md:49–55`, `backend/.ai/conventions/engineering.md:49–55`.
**Рекомендация:** ограничить применение этих workflows явно выбранной planning/TDD задачей и реальным regression risk. Сохранить reproduction для bug fixes и meaningful proof; не требовать новый тест для удаления unused field/export.
Стандартные OpenSpec skills изменять не требуется.
## F5. Opt-in OpenSpec против mandatory audit remediation
Противоречие:
- Opt-in: `specs/AGENTS.md:3–6`, `frontend/.ai/conventions/engineering.md:11–13`, `backend/.ai/conventions/engineering.md:11–13`.
- Mandatory audit-derived registry/workflow: `specs/openspec/specs/project-foundation/spec.md:9–19`.
- Обязательные snapshots/registry: `specs/openspec/specs/audit-remediation-workflow/spec.md:9–49`.
- Docs запрещает размещение audit/research reports: `docs/AGENTS.md:12–14`.
**Упростить действующую process policy**, согласовав область действия и место artifacts. Не создавать registry/SDD change автоматически для реализации этого аудита.
## F6. Custom SDD workflow остаётся тяжёлым даже без behavioral delta
- V2: семь planning artifact groups.
- При `skip_specs` остаются шесть.
- Schemas: 885 строк; templates: 335 строк.
- Семь stage instructions требуют native validation после записи.
- Apply preconditions перечисляют семь explicit approvals: `specs/openspec/schemas/wealth-ledger-sdd-v2/schema.yaml:587–589`.
Это не доказательство бесполезности анализа/QA. Однако small docs/tooling change не всегда требует отдельных System Analysis, QA Analysis, Docs Impact и sessions.
**Сначала соблюдать opt-in режим.** При отдельном пересмотре custom schema — соразмерный набор artifacts по реальному риску. Не удалять approval/data-safety boundaries вслепую.
## F7. Specs CI даёт мало гарантии за полноценный install
`specs/.gitea/workflows/verify.yml:17–22` → `npm ci` → `git diff --check`.
На чистом checkout команда не проверяет committed PR diff, semantic structure или OpenSpec artifacts. Зато npm `prepare` настраивает hooksPath: `specs/package.json:13`.
**Удалить нынешний пустой job**, предварительно установив required-check settings. Существующие native validations запускать для соответствующего изменения; этот аудит не требует нового verifier.
## F8. Что не является проблемой
- AGENTS routers уже ограничивают чтение affected topics.
- Root и nested conventions не наследуются друг в друга — разные enum/style choices не конфликт.
- Read-only archives/history не нужно удалять.
- Повторение краткого stage-specific rule допустимо, если оно нужно executor текущей стадии.
- Транзитивно избыточные schema edges нельзя удалять только по graph reachability: они могут влиять на CLI context inputs.
## Необходимые измерения
Без новых tools достаточно existing CI logs и session usage:
1. Duration каждой verify stage, cold/warm отдельно.
2. Время workspace builds в local commands/hook.
3. Частота local full verify и reinstall.
4. CI critical path и full-history scan duration.
5. Token/context volume на обычную задачу против explicitly selected SDD change.
6. Какие gates действительно находят regressions и на какой boundary.
Сейчас денежная/временная экономия не оценена численно.
# G. DELETE / SIMPLIFY / KEEP
## DELETE — 18 подтверждённых точек
Удаление подразумевает сохранение указанного более сильного или эквивалентного механизма.
1. Custom implementation `check-openapi.mjs` — заменить native check.
2. Второй TypeScript call в frontend verify.
3. `typecheck:app` stage из backend full verify — test typecheck superset оставить.
4. Повторный backend build перед SQLite в том же CI job.
5. Невызываемый `runtime-check.cjs`.
6. `useAuthenticatedQueries` и исключительно его test branch.
7. Неиспользуемый `history.retry`.
8. Неиспользуемый `isFetching` input.
9. Неиспользуемая Portfolio membership DI dependency.
10. String alias `TBANK_ACCESS` — concrete provider оставить.
11. Невызываемый `TbankAccessService.membershipWork`.
12. Неиспользуемый `candleTail` policy key.
13. Непривязанный `HourlySyncSectionDto`.
14. Неиспользуемый внешний export `totalsByCurrency`.
15. Delegation-only `brokerage-operations.controller.spec.ts`.
16. Повторный standalone anonymous `/accounts` route test.
17. Повторный image-error exercise в AccountEvents page test.
18. Пустой specs CI verify job — после проверки branch requirements.
Evidence и consequences перечислены в B–E. Это **18 элементов/стадий**, а не 18 файлов.
## SIMPLIFY
- Local gate scope и bootstrap frequency.
- Dependency graph invocation в pre-commit.
- SQLite runner regression scope.
- Exhaustive preset parity и mechanical custom lint policy.
- Metadata tests через production-used methods.
- Portfolio `request`/private test access.
- AccountEvents fake-query setup.
- Pure mapper/use-case tests.
- Три order-dependent bank cases.
- Bond-only InstrumentSearch contract.
- Motion → CSS.
- FormProvider rule по наличию consumers.
- Process specs/SDD activation.
- Specs hook activation через явное подключение вместо npm lifecycle.
- Visual SHA tables только там, где exports реально передаются отдельно от Git.
## KEEP
- Финансовая точность и business validation.
- Intent fingerprint, unique IDs, versions, tombstones, refund transaction.
- Refresh hashing/CAS, cursor signature/binding.
- Connection revision и conditional publication.
- Coverage/completeness/freshness как разные свойства.
- Independent local/CI trust boundaries.
- Per-suite DB isolation и crash/durability proofs.
- Runtime OpenAPI parity и consumer artifact freshness.
- Scanner checksum, exact image digest/revision checks.
- Deployment lock, pending marker, backup staging, explicit recovery.
- Repo-local небольшие configs/helpers вместо нового shared tooling package.
- Исторические ADR/archives.
- Реальные browser layout и Storybook isolation guarantees.
**Условный кандидат, не DELETE:** `InvestmentRefreshState` имеет ноль production callers, но четыре stories и catalog isolation consumer — `frontend/src/features/tbank-connection/ui/InvestmentRefreshState/InvestmentRefreshState.view.tsx:1–61`, `frontend/src/features/tbank-connection/ui/InvestmentRefreshState/InvestmentRefreshState.stories.tsx:1–52`, `frontend/src/test/storybook.test.tsx:11,38`. Его удаление уберёт catalog artifact; намерение owner не установлено.
# H. Top 20 Complexity Reduction Opportunities
Порядок — по ожидаемому сокращению engineering overhead. Экономия указана в механизмах/запусках, не в выдуманных секундах.
Сокращённые filenames в таблице однозначно относятся к полным путям, указанным в B–F.
| № | Файлы и доказательства | Текущий механизм → минимальное изменение | Что перестанет работать / ожидаемая экономия | Риск · Confidence |
|---:|---|---|---|---|
| **1** | FE/BE `.ai/conventions/engineering.md:58–66` | `npm ci` + full verify для любого code change → bootstrap по изменению inputs, local affected checks, full CI | Исчезнет обязательный ранний full-project proof; меньше reinstall/full suites на малую задачу | Medium · High |
| **2** | `backend/test/sqlite-regression.cjs:20–22,77–91`; `backend/test/sqlite.cjs:10–17` | Пять successful `--all` runs → по две representative suites | Теряются повторные business runs внутри runner proof; **95 → 10** successful suite executions, canonical full CI остаётся | Medium · High facts |
| **3** | `frontend/package.json:26`; `frontend/tsconfig.app.json:17`, `frontend/tsconfig.node.json:12` | Два TS calls с noEmit configs → один | Второй call не выполняется; минус один compiler invocation/full verify | Low · High |
| **4** | `backend/package.json:22–34`; typecheck configs app `7–8`, test `7–8` | App subset + test superset → test superset в aggregate | Отдельная маркировка app-stage failure; минус один nonincremental TS pass | Low · High |
| **5** | `backend/.gitea/workflows/verify.yml:84–89`; package `28,34` | Verify build, затем E2E build → после verify existing `node test/sqlite.cjs --all` | E2E stage перестанет пересобирать уже готовый output; standalone entrypoint сохранить | Low · High |
| **6** | `frontend/.husky/pre-commit:1–3`; package `25,33`; UI/icons package builds | Whole graph + workspace build на каждый commit → local/CI graph | Потеря hook-time graph feedback; минус один workspace build/graph traversal на commit | Low–Medium · High |
| **7** | `frontend/scripts/check-openapi.mjs:1–45`; CLI `125–134` | Custom temp/subprocess compare → `openapi-typescript … -o … --check` | Ничего из freshness guarantee; **45 строк custom orchestration** заменяются CLI option | Low · High; native check executed |
| **8** | `specs/package.json:9,13`; `specs/.gitea/workflows/verify.yml:17–22` | Install + clean working diff check → удалить пустой job | Теряется малоинформативный check status; минус job/install/hooks side effect | Low после branch-setting review · High |
| **9** | FE `eslint-rules.mjs:1–178`; `test-eslint.mjs:148–202,290–339` | Mechanical AST policy + exhaustive preset parity → оставить semantic boundaries и custom-exception probes | Меньше формальных style/shape guarantees; меньше custom rule/test maintenance | Medium · High facts |
| **10** | `frontend/src/app/routes/RouteTransition.tsx:3–30`; package `52`; caller `RouteFallback.tsx:70–81` | Motion для fade-in → CSS с reduced motion | Library-driven fade-in заменяется; сохранить key/mount semantics; **−1 runtime dependency** | Low–Medium · High |
| **11** | Metadata service `45–61`; persistence tests `87–382`; repository `159–170` | Test-only read/start API → tests через `request`/`savedMany`, затем удалить API/work accessor | Исчезнет unused combined operation; реальные reuse/race/restart proofs сохранить | Medium · High |
| **12** | `frontend/src/shared/api/authenticated-query.ts:15–32,61`; test `9–23` | Unused plural generic hook → удалить и test-only variant | Multi-query convenience без product consumers; меньше generic surface/test matrix | Low · High |
| **13** | Portfolio service `45–53,234–243`; value-history tests `99–111,247–256` | Unused DI + public/private alias → один `request`, прямой provider access в tests | Private test route исчезнет; queue/publication не меняется | Low · High |
| **14** | History hook `52–61,76`; initial availability `10–46` | Невостребованное action и непрочитанный input → удалить | Никакой used behavior; automatic cursor recovery/wait budget остаются | Low · High |
| **15** | InstrumentSearch view `18–31`; BondsPage view `26–113`; test `21–29` | Stock/bond renderer при одном bond consumer → bond-specific current contract | Исчезнет test-only stock mode; current product search не меняется | Low–Medium · High |
| **16** | AccountEvents test `13–28,40–205`; filters test `50–108` | Fake hooks/query shapes → existing MSW/view boundaries | Исчезнут internal-hook assertions и пять fake shapes; visible outcomes сохранить | Medium · High |
| **17** | Summary spec `11–48`; portfolio spec `48–165`; mapper `33–138` | Fake tx/background work ради чистого result → ближайший existing owner | Unit не будет exercise’ить orchestration; configured DB/publication proofs остаются | Low–Medium · High |
| **18** | Controller spec `1–18`; route test `35–61`; page image test `166–169` | Delegation-only и одинаковые assertions → удалить узкие дубли | Только internal-call/repeated fallback assertions; более сильные HTTP/route/EntryCard proofs остаются | Low · High |
| **19** | `backend/test/bank-cards.e2e-spec.ts:371–389,710–753`; setup `86–105` | Три tests с implicit prerequisites → собственный arrange через `createCard` | Теряется зависимость от suite history, не business guarantee; targeted tests дешевле понимать | Low–Medium · High |
| **20** | Specs AGENTS `3–6`; foundation `9–19`; remediation `9–49`; writing-plans `16–17,77–87`; TDD `18–45` | Mandatory artifacts/SDD/TDD rules конфликтуют с opt-in/direct work → уточнить existing activation/scope | Не будет автоматических planning/registry artifacts для каждой audit-derived правки; меньше context/approval loops | Medium · High |
Отдельные дешёвые dead-surface cuts из G можно делать при связанном изменении owner. Ради удаления одного export не нужен самостоятельный архитектурный проект.
# I. Minimal Engineering Baseline
Это предложение сократить существующий workflow, **не новая convention или набор инструментов**.
## Local development
Оставить:
- Активировать local Node version.
- Bootstrap при новом checkout или изменении dependency/toolchain inputs.
- Targeted test на cheapest reliable boundary.
- Relevant typecheck/lint.
- Semantic review affected behavior и `git diff --check`.
- Risk-triggered DB/browser/contract/durability proof.
Убрать обязательный `npm ci` и весь full-project gate после каждого code-only изменения.
Full verify остаётся доступным локально, но его обязательная независимая граница — CI.
## Pre-commit
Оставить frontend:
1. Staged Gitleaks с redaction/fail-closed.
2. Staged ESLint.
3. Commit-msg policy при подключённом hook.
Перенести whole dependency graph и workspace builds из pre-commit.
Specs `commit-msg` можно оставить; подключение hooks сделать явным вместо автоматического `prepare`. Это не отменит уже записанный hooksPath само по себе.
## CI
### Frontend
Оставить:
- Native OpenAPI freshness.
- Один TypeScript pass.
- Один ESLint run.
- Behavioral tests + coverage.
- Knip.
- Production и Storybook graph checks с сохранением различающихся semantics.
- Application и catalog builds.
- Security и commit policy.
Lint-config tests сократить до project-specific scopes/exceptions/custom behavior; изменение tooling требует соответствующего focused proof.
### Backend
Оставить:
- Generated Prisma client до checks.
- Один full-source/test typecheck.
- ESLint.
- Unit suite.
- Один build.
- Изолированные functional SQLite suites, включая runtime OpenAPI contract.
- Security и commit policy.
После уже выполненного build использовать existing runner без повторного build. Performance и historical durability остаются отдельными risk-triggered командами.
### Docs / specs
- Docs Markdown/link lint сохранить.
- Working-diff check оставить локально.
- Specs пустой verify job удалить после проверки branch requirements.
- Native OpenSpec validation использовать по изменению schema/spec/change и только в выбранном SDD scope.
- Security/release/commit mechanisms сохранить.
## Testing layers
Минимально достаточные существующие layers:
1. Pure domain/unit — деньги, normalizers, mappers.
2. RTL/MSW — UI, forms, route/session/transport behavior.
3. Configured HTTP + real SQLite — pipes, guards, transactions, CAS, persistence.
4. Browser — geometry/lifecycle, которые JSDOM не доказывает.
5. Image/historical/deploy — соответствующие packaging/data-recovery risks.
Удалять одинаковый proof, а не целый layer.
## Architecture constraints
Оставить:
- Production не зависит от test code.
- Package не зависит от app.
- FSD public API/layer/cycle boundaries.
- Presentation не выполняет application fetching.
- Story graph не достигает application data.
- Pure domain не зависит от persistence/transport.
Не добавлять interface перед каждым concrete provider, общий worker framework или новые архитектурные checks.
## AI instructions
Оставить:
- Local AGENTS router.
- Owner/callers/invariants до изменения.
- Сохранение чужих изменений.
- Context по affected topics.
- Meaningful verification evidence.
- Opt-in OpenSpec.
Сократить:
- Universal plan-file creation.
- Новый failing test для каждого mechanical refactor.
- Mandatory full reinstall/full verify на любую правку.
- Blanket FormProvider/VM/helper/style mechanics без current responsibility.
- Повторные process authority descriptions и обязательные artifacts с конфликтующим размещением.
---
## Итог
**Наиболее выгодный путь: сократить обязательную частоту и scope проверок → удалить доказанную невостребованную поверхность → упростить test boundaries → согласовать процессную authority.**
Не требуется менять финансовую архитектуру, добавлять инструменты или объединять независимые repositories.
**Net:** точный line delta не подготовлен; подтверждена замена 45-строчного OpenAPI checker native CLI, 18 точек удаления и возможность убрать одну runtime dependency после CSS-замены. Экономия времени и токенов требует измерений.