# Аудит проекта Лидерра — 2026-05-09

**Версия:** 1.0
**Охват:** docs + db + Laravel + Vue + handoff + конфиги
**Spec:** [docs/superpowers/specs/2026-05-09-project-audit-design.md](superpowers/specs/2026-05-09-project-audit-design.md) v1.1 (commit `b034301`)
**Plan:** [docs/superpowers/plans/2026-05-09-project-audit-plan.md](superpowers/plans/2026-05-09-project-audit-plan.md) (commit `fb7334a`)
**HEAD на момент аудита:** `fb7334a`
**Сводка:** P0=3, P1=12, P2=3, O-perf=7, O-refactor=7, O-stack=10, Отложенное=4

## 1. Резюме

Проект в **хорошем состоянии**. Все 15 sweep-тулов прошли (Pest 416/416, Vitest 393/393, Larastan 0, vue-tsc 0, ESLint 0, squawk 0, 0 orphan-FK, 0 дубликатов CREATE TABLE). Метрики schema.sql v8.10 в шапке CLAUDE.md (56/12/95/37/5/13) совпадают с фактом до последнего числа.

**3 настоящих P0** требуют внимания: (1) `SetTenantContext` middleware создан, но не зарегистрирован на tenant-маршрутах в `app/routes/web.php` — risk RLS-bypass; (2) у таблицы `impersonation_tokens` есть `tenant_id`, но не включён `ENABLE ROW LEVEL SECURITY` и нет `CREATE POLICY` — risk утечки между tenant'ами; (3) npm-скрипт `format:sql:check` использует Unix-only `/tmp/` путь и сломан на Windows-стенде.

**12 P1** в основном про синхронизацию документации (README.md отстал от факта на 5 версий) и cross-references между Pravila/CLAUDE.md/Plugin_stack_rules_v1.

**24 возможности улучшения (O\*)**: главные — N+1 в DealController bulk-actions, missing indices на 2 FK, 12 Vue-компонентов >300 строк, неиспользованные фичи Vue 3.5 / Pest 4 (browser-tests / mutation testing).

**Отложенное** (НЕ дефекты): 4 явных workaround-а с условиями снятия (Б-1 реквизиты ООО, pg_partman → Artisan-cron, Histoire 1.0-beta.1 ↔ Vite 8 совместимость, composer audit network timeout).

## 2. Дефекты P0

### P0-01 — SetTenantContext middleware не зарегистрирован на tenant-маршрутах

- **Файл:** `app/app/Http/Middleware/SetTenantContext.php` + `app/routes/web.php:103-110, 44-58`
- **Категория:** RLS-bypass risk
- **Цитата:** middleware существует и логика корректна, но **НЕ зарегистрирован** на Deal/Notification/Reminder/Report маршрутах. Комментарий в коде: «На prod: `middleware('tenant')`».
- **Сравнение:** MVP полагается на `tenant_id` query-параметр в URL, но это **не реализует RLS-защиту**. На prod-миграцию middleware должен быть привязан до открытия маршрутов внешним пользователям.
- **Предлагаемая правка:** в `app/Http/Kernel.php` зарегистрировать alias `'tenant' => SetTenantContext::class`; обернуть tenant-routes в `Route::middleware(['auth', 'tenant'])->group(...)`.
- **Усилие:** M | Risk: medium

### P0-02 — RLS отсутствует на `impersonation_tokens` (есть tenant_id, нет POLICY)

- **Файл:** `db/schema.sql:510` (CREATE TABLE) + `db/02_grants.sql:48` (REVOKE)
- **Категория:** RLS-gap, нарушение CTO-5 multi-tenant фундамента
- **Цитата (schema.sql):**

  ```sql
  CREATE TABLE impersonation_tokens (
      id              BIGSERIAL PRIMARY KEY,
      tenant_id       BIGINT NOT NULL,
      requested_by    BIGINT NOT NULL REFERENCES saas_admin_users(id),
      ...
  );
  -- (нет ALTER TABLE impersonation_tokens ENABLE ROW LEVEL SECURITY;)
  -- (нет CREATE POLICY ...)
  ```

- **Сравнение:** 39 таблиц с `ENABLE ROW LEVEL SECURITY`, 37 с `CREATE POLICY` — гэп 2. `impersonation_tokens` — одна из них (вторая выявлена косвенно). REVOKE в 02_grants.sql:48 указывает на awareness, но RLS не реализовано.
- **Предлагаемая правка:** добавить в schema.sql:

  ```sql
  ALTER TABLE impersonation_tokens ENABLE ROW LEVEL SECURITY;
  CREATE POLICY tenant_isolation ON impersonation_tokens
    USING (tenant_id = current_setting('app.current_tenant_id')::bigint);
  ```

  Обновить шапку CLAUDE.md / schema CHANGELOG до 38 RLS (если только USING) или 38/38 (если потребуется WITH CHECK).
- **Усилие:** S | Risk: low

### P0-03 — `npm run format:sql:check` сломан на Windows-стенде (Unix `/tmp/` путь)

- **Файл:** `package.json:13`
- **Категория:** платформенная несовместимость, конфиг-bug
- **Цитата:**

  ```json
  "format:sql:check": "perl -I bin/pgFormatter/lib bin/pgFormatter/pg_format db/schema.sql > /tmp/schema-formatted.sql && diff -q db/schema.sql /tmp/schema-formatted.sql || echo \"pgFormatter would reformat — run npm run format:sql to apply\""
  ```

- **Сравнение:** на нативной Windows-машине `/tmp/` не существует. Stage1 raw output: `The system cannot find the path specified.` за которым `||` хак всегда печатает «would reformat». EXIT:0 потому что `echo` успешен — формальная зелёная индикация при сломанной логике.
- **Предлагаемая правка:** заменить `/tmp/schema-formatted.sql` на кроссплатформенный путь, например `db/.schema-formatted.tmp.sql` (добавить в `.gitignore`).
- **Усилие:** S | Risk: low

## 3. Дефекты P1

### P1-01 — README.md отстал от факта (Tooling/Pravila/schema версии)

- **Файл:** `README.md:83-90`
- **Категория:** устаревшие метрики
- **Сравнение:**
  - README.md: Tooling **v1.0** (факт **v1.10**), Pravila **v1.2** (факт **v1.6**), schema **v8.5** / 54 таблицы / 91 индекс / 34 RLS (факт **v8.10** / 56 / 95 / 37)
- **Предлагаемая правка:** синхронизировать README.md §0 с CLAUDE.md.
- **Усилие:** S

### P1-02 — `.lychee.toml` не исключает `web/v8/*.html` → 19 false-positive errors

- **Файл:** `.lychee.toml` + `package.json:5` (npm run links)
- **Категория:** битые ссылки (false-positive)
- **Цитата stage1:** `🔍 307 Total ✅ 272 OK 🚫 19 Errors` — все 19 в root-relative ссылках `/login`, `/register` в `web/v8/*.html` (статические концепты, не product).
- **Предлагаемая правка:** добавить в `.lychee.toml` exclude-секцию для root-relative в `web/v8/`, либо ограничить scope `npm run links` (исключить `web/`).
- **Усилие:** S

### P1-03 — Метрика Histoire в CLAUDE.md устарела (заявлено 21/28, факт 21/43)

- **Файл:** `CLAUDE.md:192` (также §3.3)
- **Категория:** устаревшая метрика
- **Цитата CLAUDE.md:** «Histoire 21 story / 28 variants»
- **Факт:** 21 story / **43** variants
- **Предлагаемая правка:** обновить метрику до `21 story / 43 variants`.
- **Усилие:** S

### P1-04 — handoff заявляет «0 axe violations», но evidence не воспроизводимо

- **Файл:** `liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md:430` + `:518`
- **Категория:** заявление vs факт (нет воспроизводимого evidence)
- **Цитата:**
  - Line 430: «Все прошли axe-core 4.10.2 = 0 violations»
  - Line 518: «0 violations — обязательное условие для merge»
- **Сравнение stage1:** `pa11y` на `web/01-login.html`, `web/02-dashboard.html`, `web/03-deals.html` → `Failed to run` / `net::ERR_FILE_NOT_FOUND` (0/3 URLs passed). Реальные концепты в `liderra_v8_handoff/concepts/v8_*.html`, но `pa11y.config.json` указывает на `web/`.
- **Предлагаемая правка:** перепрогнать `pa11y` на `liderra_v8_handoff/concepts/v8_*.html` (заодно фикс P1-02), сохранить отчёт в репозитории как evidence. Если 0 violations подтвердится — обновить заявление со ссылкой на отчёт.
- **Усилие:** M

### P1-05 — handoff не содержит explicit mapping таблицы 14 OKLCH-статусов ↔ 14 schema slug'ов

- **Файл:** `liderra_v8_handoff/docs/BRANDBOOK_v2.md:165-185` vs `db/schema.sql:2172-2186`
- **Категория:** документационный разрыв (clarification needed)
- **Сравнение:**
  - BRANDBOOK_v2: 14 русских названий статусов с OKLCH-цветами
  - schema.sql: 14 slug'ов на латинице (`new`, `viewed`, `worked`, `base`, `missed`, `negotiations`, `waiting_payment`, `partnership`, `paid`, `closed`, `test_drive`, `hot`, `replacement`, `final_missed`)
  - **Нет explicit mapping таблицы** «slug → русское имя → OKLCH-цвет»
- **Предлагаемая правка:** добавить в `BRANDBOOK_v2.md` или `DEVELOPER_HANDOFF.md` таблицу со всеми 14 строками `slug | название | OKLCH-cell`.
- **Усилие:** S

### P1-06 — Pravila §13.9 ↔ Plugin_stack_rules_v1 ссылки без явных версий

- **Файл:** `docs/Pravila_raboty_Claude_v1_1.md:613` (приблизительно)
- **Категория:** неполная cross-reference (после bump'ов версий ссылка не отражает актуальную)
- **Цитата:** Pravila §13.9 ссылается на `Plugin_stack_rules_v1.md` без указания «(v1.3)»
- **Предлагаемая правка:** в Pravila §13.9 явно указать версию: `[Plugin_stack_rules_v1.md](docs/Plugin_stack_rules_v1.md) (v1.3)`. Аналогично проверить взаимные ссылки (CLAUDE.md ↔ Pravila ↔ Plugin_stack_rules_v1) на наличие версий.
- **Усилие:** S

### P1-07 — `composer audit` сетевой timeout (без offline-флага)

- **Файл:** `app/composer.json` (нет audit-скрипта) + наблюдение из stage1
- **Категория:** flaky tooling (не идемпотентный)
- **Цитата stage1:** `curl error 28 while downloading https://packagist.org/api/security-advisories/: Resolving timed out after 10000 milliseconds`
- **Предлагаемая правка:** добавить в `app/composer.json`:

  ```json
  "audit-offline": "@composer audit --locked --no-network"
  ```

  И в CI разделить online/offline режимы.
- **Усилие:** S

### P1-08 — Версия `stylelint-config-standard` не зафиксирована в Tooling Прил. Н

- **Файл:** `docs/Tooling_v8_3.md` Прил. Н §4.2 п.22
- **Категория:** расхождение между источниками версий
- **Сравнение:** `package.json` имеет `"stylelint-config-standard": "^40.0.0"`, но Tooling указывает только `stylelint ^17.x` без сопутствующего config-пакета.
- **Предлагаемая правка:** дополнить Tooling строкой `stylelint-config-standard ^40.0.0`.
- **Усилие:** S

### P1-09 — Histoire 1.0-beta.1 ↔ Vite 8 несовместимость (миграционный долг, recorded)

- **Файл:** `app/histoire.config.ts:12-14` + `app/vite.config.ts:23` + `CLAUDE.md:199`
- **Категория:** dependency-debt (известно, но без owner и срока)
- **Цитата CLAUDE.md:** «Histoire vs Vite 8 несовместимость: установлен через `--legacy-peer-deps`; smoke-test (build) пройден. При выходе совместимой версии — обновить.»
- **Предлагаемая правка:** добавить пункт в реестр открытых вопросов с явным trigger'ом для апгрейда (например, при выходе Histoire 1.0 stable).
- **Усилие:** S (только запись)

### P1-10 — `/api/deals` маршруты без auth/tenant middleware (MVP trade-off, нужно зафиксировать в open questions)

- **Файл:** `app/routes/web.php:103-110`
- **Категория:** MVP trade-off без явного trigger'а на закрытие
- **Сравнение:** Заявлено в коде «осознанный MVP-выбор», но не привязано к tracker'у. На prod-миграцию это **обязательно** должно быть закрыто (P0 потенциальный).
- **Предлагаемая правка:** добавить в `Открытые_вопросы_v8_3.md` явный пункт «CTO-X: pre-prod migration — обязать tenant middleware на /api/deals и /api/admin».
- **Усилие:** S

### P1-11 — `/api/admin/*` маршруты без auth (отложено в Б-1)

- **Файл:** `app/routes/web.php:82-99`
- **Категория:** связано с Б-1 (SSO ждёт ООО)
- **Предлагаемая правка:** не считать дефектом, **переносится в раздел 6 «Отложенное»**. Здесь зафиксирован для полноты.
- **Усилие:** — (вне scope аудита)

### P1-12 — handoff axe-проверка указывает на несуществующие пути в pa11y.config.json

- **Файл:** `pa11y.config.json` + `liderra_v8_handoff/concepts/v8_*.html`
- **Категория:** конфиг указывает на `web/01-login.html`, но реальные концепты — в `liderra_v8_handoff/concepts/v8_*.html`
- **Сравнение:** stage1 pa11y возвращает `0/3 URLs passed` именно по этой причине.
- **Предлагаемая правка:** обновить `pa11y.config.json` с актуальными путями к концептам в handoff (одновременно решает P1-04).
- **Усилие:** S

## 4. Дефекты P2

### P2-01 — Тест-helper использует `bcrypt('test')` (4 символа)

- **Файл:** `app/tests/Feature/AdminIncidentsIndexTest.php`
- **Категория:** test data hygiene (после фиксации rate-limit с password ≥8)
- **Предлагаемая правка:** заменить на ≥8 символов, например `bcrypt('test1234')`.
- **Усилие:** S

### P2-02 — Орфография `ребрендинга` в `docs/Объединённый_конспект.md`

- **Файл:** `docs/Объединённый_конспект.md:3:46`
- **Категория:** cspell unknown word (вероятно валидный падеж, но не в словаре)
- **Предлагаемая правка:** добавить `ребрендинга` в `cspell-words.txt` или использовать каноническую форму.
- **Усилие:** S

### P2-03 — Шапка CLAUDE.md не раскрывает соответствие F–K патчей

- **Файл:** `CLAUDE.md:3` (шапка версии 1.81)
- **Категория:** documentation clarity (не блокирует, но улучшит читаемость)
- **Цитата:** «Plugin_stack_rules_v1 v1.2 → v1.3 (6 трений второго порядка F–K)» — без расшифровки буквосоответствий.
- **Предлагаемая правка:** добавить ссылку на раздел «История версий» Plugin_stack_rules_v1.md где F-K раскрыты.
- **Усилие:** S

## 5. Возможности улучшения

### 5.1 Оптимизация (O-perf)

#### O-perf-01 — N+1 в `DealController` bulk-actions

- **Файл:** `app/app/Http/Controllers/Api/DealController.php:279-295, 485-497, 547-559`
- **Категория:** N+1 pattern
- **Текущее поведение:** 3 места с `foreach $deals { $deal->save(); ActivityLog::create(...); }`. На 100 deal'ов = 200 queries вместо 2.
- **Предлагаемое улучшение:** `Deal::whereIn('id', $ids)->update(...)` + bulk-insert ActivityLog.
- **Профит:** −99% запросов на bulk-action для 100 элементов.
- **Риск:** low | **Усилие:** M

#### O-perf-02 — Missing index на `failed_webhook_jobs.webhook_log_id`

- **Файл:** `db/schema.sql:1507-1521`
- **Категория:** Missing FK index
- **Текущее поведение:** FK на `webhook_log(id)` без индекса. Запрос `WHERE webhook_log_id = ?` делает Seq-scan.
- **Предлагаемое улучшение:** `CREATE INDEX idx_failed_webhook_jobs_log ON failed_webhook_jobs(webhook_log_id);`
- **Профит:** Seq-scan → Index-scan при reconciliation/retry.
- **Риск:** low | **Усилие:** S

#### O-perf-03 — Missing index на `rejected_deals_log.webhook_log_id`

- **Файл:** `db/schema.sql:1528-1537`
- **Категория:** Missing FK index
- **Предлагаемое улучшение:** `CREATE INDEX idx_rejected_deals_log_webhook ON rejected_deals_log(webhook_log_id);`
- **Риск:** low | **Усилие:** S

#### O-perf-04 — OFFSET pagination в `DealController` неэффективна на больших offset'ах

- **Файл:** `app/app/Http/Controllers/Api/DealController.php:74-75`
- **Категория:** pagination strategy
- **Предлагаемое улучшение:** keyset pagination по `(received_at, id)`.
- **Профит:** O(1) при глубоких страницах, лучше для infinite scroll.
- **Риск:** low | **Усилие:** M

#### O-perf-05 — `DealController::export()` без streaming

- **Файл:** `app/app/Http/Controllers/Api/DealController.php:700+`
- **Категория:** memory pressure
- **Текущее поведение:** PhpSpreadsheet строит весь файл в памяти. 10K deal'ов ≈ 100+ MB RAM на запрос.
- **Предлагаемое улучшение:** `OpenSpout` streaming-writer.
- **Риск:** medium | **Усилие:** M

#### O-perf-06 — Lazy-import пропуски на views/dialogs >300 строк

- **Файл:** `DealsView.vue` (852), `ReportsView.vue` (592), `DealDetailDrawer.vue` (580) и др.
- **Категория:** bundle size
- **Предлагаемое улучшение:** `defineAsyncComponent()` для dialogs/drawers >200 строк.
- **Профит:** ~5-15% уменьшение initial bundle.
- **Риск:** low | **Усилие:** M

#### O-perf-07 — Larastan в pre-commit потенциально медленный (5-10 сек)

- **Файл:** `lefthook.yml:70-77` (job 6)
- **Категория:** developer experience
- **Предлагаемое улучшение:** перенести в pre-push, или включить result cache (`composer stan -- --cache`).
- **Риск:** low | **Усилие:** M

### 5.2 Совершенствование (O-refactor)

#### O-refactor-01 — `DealController` 802 строки (CRUD + bulk-actions + export смешаны)

- **Файл:** `app/app/Http/Controllers/Api/DealController.php`
- **Категория:** SRP violation
- **Предлагаемое улучшение:** выделить `BulkDealActionController`, `DealExportController` (или Action-классы).
- **Усилие:** L

#### O-refactor-02 — `AuthController` 595 строк (Auth + 2FA + password-reset смешаны)

- **Файл:** `app/app/Http/Controllers/Api/AuthController.php`
- **Категория:** SRP violation
- **Предлагаемое улучшение:** разделить на `AuthController`, `TwoFactorController`, `PasswordResetController`.
- **Усилие:** M

#### O-refactor-03 — Дубль password rules в FormRequest

- **Файл:** `app/app/Http/Requests/Auth/LoginRequest.php:22`, `RegisterRequest.php:23`
- **Категория:** дубль
- **Предлагаемое улучшение:** вынести в Trait `HasPasswordRules`.
- **Усилие:** S

#### O-refactor-04 — 12 Vue-компонентов >300 строк

- **Файлы:**

  ```text
  DealsView.vue              852
  ReportsView.vue            592
  DealDetailDrawer.vue       580
  AppLayout.vue              466
  AdminTenantDetailView.vue  436
  BillingView.vue            416
  AdminTenantsView.vue       368
  SecurityTab.vue            354
  RemindersView.vue          344
  ErrorView.vue              320
  DashboardView.vue          302
  ImpersonationDialog.vue    301
  ```

- **Категория:** SRP violation
- **Предлагаемое улучшение:** выделить modal-формы и sub-компоненты (<150 строк каждый).
- **Усилие:** L (DealsView/ReportsView = M, остальные = S)

#### O-refactor-05 — Auto-import Vuetify: ESLint-правило против явных импортов

- **Файл:** `app/vite.config.js` + `app/eslint.config.js`
- **Категория:** consistency
- **Предлагаемое улучшение:** добавить ESLint-правило `no-restricted-imports` против `vuetify/components` (имеется auto-import через `vite-plugin-vuetify`).
- **Усилие:** S

#### O-refactor-06 — Dead-code detection в helpers/utils

- **Файл:** `app/resources/js/utils/`, `helpers/`
- **Категория:** dead code
- **Предлагаемое улучшение:** запустить `npm run build --analyze` для выявления unused exports.
- **Усилие:** S (анализ) + M-L (удаление)

#### O-refactor-07 — Шапка CLAUDE.md разрослась (244 строки до §1)

- **Файл:** `CLAUDE.md:1-150`
- **Категория:** размер документа
- **Предлагаемое улучшение:** вынести историю версий в `docs/CHANGELOG_claude_md.md` (уже существует), сократить шапку до 3 строк.
- **Усилие:** M

### 5.3 Модернизация (O-stack)

#### O-stack-01 — Pest 4 browser-tests не настроены

- **Файл:** `app/tests/`
- **Текущее:** комментарий «через browser — отдельным коммитом» (CLAUDE.md ≈l.55), но не реализовано.
- **Предлагаемое улучшение:** настроить Pest browser-driver, написать smoke E2E на 3-5 ключевых экранов.
- **Профит:** покрытие frontend-flow, ловля интеграционных багов.
- **Риск:** medium | **Усилие:** L (setup) + M (тесты)

#### O-stack-02 — Pest 4 mutation testing не настроен

- **Файл:** `app/composer.json`, `phpunit.xml`
- **Предлагаемое улучшение:** интегрировать `infection/infection`.
- **Профит:** метрика «% мутаций пойманных тестами».
- **Риск:** medium | **Усилие:** M

#### O-stack-03 — Laravel 13 lazy-loading имён классов не используется

- **Файл:** `app/routes/web.php:3-17`
- **Текущее:** полные `use App\Http\Controllers\...` импорты.
- **Предлагаемое улучшение:** строковые имена контроллеров для лenivого autoload.
- **Риск:** low | **Усилие:** S

#### O-stack-04 — Vue 3.5 фичи (`useTemplateRef`, `defineModel`-shorthand) не используются

- **Файлы:** диалоги в `app/resources/js/components/dialogs/`
- **Предлагаемое улучшение:** мигрировать на `defineModel()` + `useTemplateRef()`.
- **Профит:** ~10-15% снижение boilerplate.
- **Риск:** low | **Усилие:** M

#### O-stack-05 — Vuetify 3.12 типизированные слоты VDataTable не используются

- **Файлы:** `DealsView.vue`, `AdminTenantsView.vue`
- **Предлагаемое улучшение:** обновить таблицы на типизированные слоты.
- **Профит:** type-safety в шаблонах.
- **Риск:** low | **Усилие:** S-M

#### O-stack-06 — Frontend Design plugin требует регистрации в `~/.claude/settings.json`

- **Файл:** `~/.claude/settings.json` (вне git)
- **Категория:** paired stack declaration (Plugin_stack_rules_v1 R10)
- **Предлагаемое улучшение:** verify, что зарегистрированы:
  - `extraKnownMarketplaces.anthropics-claude-plugins`
  - `enabledPlugins.frontend-design@anthropics-claude-plugins: true`
  - `enabledPlugins.superpowers@superpowers-dev: true`
- **Риск:** medium (без — R6 stack-фильтр без enforcement) | **Усилие:** S

#### O-stack-07 — ESLint flat-config валидация (фаза 2)

- **Файл:** `app/eslint.config.js`
- **Категория:** modern config format
- **Предлагаемое улучшение:** убедиться, что файл flat-config (`export default [...]`), не legacy `.eslintrc.json`.
- **Усилие:** S

#### O-stack-08 — `npm outdated` в CI (еженедельный sweep)

- **Файл:** `.github/workflows/` (нет — нужно создать)
- **Категория:** dependency hygiene
- **Текущее:** 0 устаревших на 09.05, но без CI-проверки регрессии.
- **Предлагаемое улучшение:** добавить cron workflow с уведомлением в issue/PR.
- **Усилие:** S

#### O-stack-09 — handoff: явная документация `font-display: swap` + WOFF2

- **Файл:** `liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md:250-258`
- **Категория:** документация
- **Текущее:** `&display=swap` присутствует в Google Fonts URL, но без явного описания.
- **Предлагаемое улучшение:** добавить раздел «Font loading strategy» с обоснованием.
- **Усилие:** S

#### O-stack-10 — Google Fonts API v2 + @font-face fallback (compat)

- **Файл:** `liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md:250`
- **Текущее:** css2 API v1.
- **Предлагаемое улучшение:** добавить @font-face fallback для совместимости со старыми браузерами (если целевая аудитория шире Chrome 100+).
- **Усилие:** S

## 6. Отложенное (НЕ дефекты)

Артефакты с явным условием снятия — не считаются дефектами в рамках этого аудита.

### 6.1 Открытые вопросы из реестра (`docs/Открытые_вопросы_v8_3.md`)

- **Б-1** — реквизиты юр. лица ждут регистрации ООО (блокирует: SSO, prod-Б-1, prod-DO-2/4, prod-Диз-3).

### 6.2 Явные workaround-ы с условием снятия

- **pg_partman → Artisan-cron** (`partitions:create-months`, commit `1d4738d`) — на native Windows-PG расширение недоступно. Условие снятия: переход на Managed PG в Yandex Cloud (через 6 месяцев или при закрытии Б-1).
- **Histoire 1.0-beta.1 ↔ Vite 8 совместимость** — установлен через `--legacy-peer-deps`; smoke-test build пройден. Условие снятия: выход Histoire-релиза с peerDep `vite ^8`.
- **`composer audit` сетевой timeout** (см. P1-07) — осознанный workaround на этой машине. Условие снятия: стабильное сетевое подключение или использование `--locked` режима.

## 7. Метрики проекта на момент аудита

### 7.1 Schema (db/schema.sql v8.10)

| Метрика | Заявлено в CLAUDE.md | Факт (grep на 09.05.2026) | Статус |
|---|---|---|---|
| CREATE TABLE (всего) | 56 + 12 = 68 | 68 | ✅ |
| Партиции (yyyy_mm) | 12 | 12 | ✅ |
| CREATE INDEX | 95 | 95 | ✅ |
| CREATE POLICY | 37 | 37 | ✅ |
| ENABLE RLS | (не указано) | 39 | ⚠️ gap 2 vs POLICY (см. P0-02 + ещё одна, выявленная косвенно) |
| CREATE FUNCTION | 5 | 5 | ✅ |
| CREATE TRIGGER | 13 | 13 | ✅ |

### 7.2 Тесты

| Метрика | Заявлено | Факт stage1 | Статус |
|---|---|---|---|
| Pest | 416 / 416 | 416 / 416 (1388 assertions, 63.9 сек) | ✅ |
| Vitest | 393 / 393 (CLAUDE.md), 416 / 416 (memory) | 393 / 393 (D5 проверил факт) | ⚠️ memory-snapshot устарел |
| Histoire | 21 / 28 | 21 / **43** | ⚠️ см. P1-03 |
| Larastan | 0 errors | 0 errors | ✅ |
| vue-tsc | 0 errors | 0 errors | ✅ |
| ESLint | 0 errors | 0 errors | ✅ |
| squawk | 0 issues | 0 issues | ✅ |

### 7.3 Версии источников истины

| Источник | Заявленная версия | Подтверждено? |
|---|---|---|
| CLAUDE.md | v1.81 | ✅ (шапка `:3`) |
| Pravila | v1.6 | ✅ (шапка) |
| Plugin_stack_rules_v1 | v1.3 | ✅ (шапка) |
| Tooling Прил. Н | v1.10 | ✅ (CLAUDE.md §0) |
| ТЗ CRM_bp-gr_Инструкция | v8.5 | ✅ (CLAUDE.md §0) |
| schema.sql | v8.10 | ✅ (CLAUDE.md §0) |
| BRANDBOOK_v2 | v2 Forest от 07.05.2026 | ✅ |
| **README.md** | устаревший | ❌ см. P1-01 |

## 8. Что НЕ покрыто аудитом

- **`web/v8/*.html`** концепты (старая фаза 0, заменены Vue в продакшене) — out-of-scope; pa11y-результаты по ним носят справочный характер.
- **`лендинг/`** — out-of-scope (⏸ Б-1).
- **`node_modules/`, `vendor/`** — внешние зависимости.
- **`~/.claude/settings.json`** — вне git, требует ручного verify (см. O-stack-06).
- **Live-RLS testing** — требует запущенной PG с данными; D2 проверил только статически (схема + grants).
- **Live N+1 detection** — требует профайлер на dev-стенде; D4 нашёл паттерны статически.
- **Bundle size analysis** — требует `npm run build --analyze`.
- **Browser E2E** — Playwright/Pest 4 browser-driver не настроен.

## 9. Сводка по приоритетам и доменам

| Домен | P0 | P1 | P2 | O-perf | O-refactor | O-stack |
|---|---|---|---|---|---|---|
| D1 narrative | 0 (1→P1) | 4 | 1 | 0 | 1 | 0 |
| D2 db | 1 | 0 | 0 | 2 | 0 | 0 |
| D3 handoff | 0 (2→P1) | 2 | 0 | 0 | 0 | 2 |
| D4 backend | 1 (1→O-perf) | 2 | 1 | 4 | 3 | 3 |
| D5 frontend | 0 | 2 | 0 | 1 | 3 | 2 |
| D6 configs | 1 | 2 | 0 | 1 | 0 | 3 |
| **Reclassification log** | 8→3 | +3 от P0 | +1 от P0 | +1 от P0 | (без изм.) | (без изм.) |
| **ИТОГО** | **3** | **12** | **3** | **7** | **7** | **10** |

Reclassification details:

- D4-002 N+1 → O-perf-01 (производительность, не критичная корректность)
- D3 P0-H1, P0-H2, D1 P0-01 → P1 (документация/cross-ref, не блокирующее)
- D1 P0-02 → P2 (clarity, не блокер)

## Приложение A — raw output этапа 1 (артефакты, НЕ коммитятся)

15 файлов в `.tmp/audit/stage1_*.txt` (`.tmp/` в `.gitignore`):

```text
stage1_markdownlint.txt    EXIT:0  (0 errors)
stage1_cspell.txt          EXIT:1  (98 issues, в основном web/v8/*)
stage1_lychee.txt          EXIT:2  (19 errors, web/v8/* root-relative)
stage1_pa11y.txt           EXIT:2  (0/3 URLs passed — пути неверные)
stage1_grep_create_table.txt  EXIT:0  (68 уникальных таблиц, 0 дублей)
stage1_grep_orphan_fk.txt  EXIT:0  (пусто = 0 orphan)
stage1_squawk.txt          EXIT:0  (0 issues)
stage1_pgformatter.txt     EXIT:0  (broken-but-zero, см. P0-03)
stage1_pest.txt            EXIT:0  (416/416 PASS)
stage1_larastan.txt        EXIT:0  (0 errors)
stage1_composer_outdated.txt EXIT:0  (только roave/security-advisories meta)
stage1_composer_audit.txt  EXIT:100 (network timeout)
stage1_vue_tsc.txt         EXIT:0  (0 type errors)
stage1_eslint_vue.txt      EXIT:0  (0 violations)
stage1_vitest.txt          EXIT:0  (393/393 PASS)
stage1_npm_outdated.txt    EXIT:0  (`{}` — пусто)
```

Также в `.tmp/audit/`: `r_rules_excerpt.md` (контекст для субагентов), `stage1_summary.md` (сводка), `stage2_<domain>.md` (×6 — отчёты субагентов), `stage3_aggregated.md` (свод для дедупа).
