Files
portal/docs/audit_2026-05-09.md
T
Дмитрий b6ae8dd641 docs(audit): сводный отчёт аудита проекта на 2026-05-09 — P-дефекты + O-возможности
По spec v1.1 (b034301) и plan'у 2026-05-09-project-audit-plan.md (fb7334a):
- 4 этапа: 15 CLI-тулов parallel + 6 субагентов parallel + консолидация + self-review.
- Покрытие: docs (16 .md + Pravila/Plugin_stack_rules_v1/Tooling/CLAUDE.md/README) + db (4 файла) + handoff + Laravel + Vue + 10 конфигов корня.
- Категории: P0/P1/P2 + O-perf/O-refactor/O-stack + Отложенное (открытые вопросы + workaround-ы).
- R-CHECKS под Plugin_stack_rules_v1 v1.3 включены в чеклист и DoD.

Сводка: P0=3, P1=12, P2=3, O-perf=7, O-refactor=7, O-stack=10, Отложенное=4.

Главные находки P0:
- SetTenantContext middleware не зарегистрирован на tenant-маршрутах (RLS-bypass risk).
- impersonation_tokens без RLS+POLICY (есть tenant_id, gap 2 vs ENABLE RLS).
- format:sql:check сломан на Windows (Unix /tmp/ путь).

Self-review (verification-before-completion): file:line ссылки сверены, метрики schema перепроверены grep'ом (68/12/95/37/5/13 ✓), 0 placeholders, исправлен один номер строки D2 (510 не 639).
Independent code review: VERDICT: PASS (.tmp/audit/stage4_code_review.md).

Сам отчёт — диагностический. Реализация правок — отдельный flow по решению заказчика.
Stage 1-4 артефакты в .tmp/audit/ НЕ коммитятся (.gitignore).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 17:40:25 +03:00

523 lines
33 KiB
Markdown
Raw 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.
# Аудит проекта Лидерра — 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` (свод для дедупа).