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>
This commit is contained in:
@@ -851,6 +851,8 @@ SRP
|
||||
тулами
|
||||
тул
|
||||
коммитится
|
||||
OKLCH
|
||||
ребрендинга
|
||||
|
||||
# v1.81 — Plugin Stack Rules v1.3 (Frontend Design + Superpowers paired stack)
|
||||
инвокация
|
||||
|
||||
@@ -0,0 +1,522 @@
|
||||
# Аудит проекта Лидерра — 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` (свод для дедупа).
|
||||
Reference in New Issue
Block a user