# Spec: Спринт 1 «Hygiene» — исправление дефектов аудита 2026-05-09

**Версия:** 1.0
**Дата:** 09.05.2026
**Автор:** Claude Code (skill: superpowers:brainstorming)
**Заказчик:** Дмитрий
**Статус:** черновик, ждёт review заказчика

## 1. Цель

Закрыть быстрые/безопасные находки аудита [docs/audit_2026-05-09.md](../../audit_2026-05-09.md) (commit `b6ae8dd`):

- **Все 3 P0** (security/RLS + сломанный конфиг) — обязательно.
- **10 из 12 P1** (P1-10/P1-11 → реестр, не правки).
- **Все 3 P2** (мелочи).
- **6 low-risk O-\*** (effort=S, risk=low): 2 missing FK indices, password rules trait, ESLint anti-vuetify-import, npm outdated CI workflow, font-display docs.

**Не входит в Sprint 1:** все большие рефакторинги (DealController/AuthController/12 Vue-компонентов >300 строк), все O-perf кроме индексов, любые миграции стека (Pest 4 browser-tests, Vue 3.5 фичи). Это Спринт 2/3 — отдельный flow.

**Wall-clock:** 3-5 часов агентов в субагентном режиме.

## 2. Scope — 22 правки + 3 записи в реестр

### Дефекты (16)

- **3 P0:** P0-01, P0-02, P0-03
- **10 P1:** P1-01, P1-02, P1-03, P1-04, P1-05, P1-06, P1-07, P1-08, P1-09, P1-12
- **3 P2:** P2-01, P2-02, P2-03

### Out-of-scope (2 P1 → реестр без правок)

- **P1-10** (auth на /api/deals) — добавить в `docs/Открытые_вопросы_v8_3.md` как новый CTO-вопрос pre-prod migration.
- **P1-11** (auth на /api/admin) — уже = Б-1; добавить cross-link.

### Возможности low-risk (6 из 24 O-\*)

- **O-perf-02:** index `failed_webhook_jobs.webhook_log_id`
- **O-perf-03:** index `rejected_deals_log.webhook_log_id`
- **O-refactor-03:** `HasPasswordRules` trait для FormRequest
- **O-refactor-05:** ESLint `no-restricted-imports` rule против `vuetify/components`
- **O-stack-08:** GitHub Actions workflow с `npm outdated` (еженедельно)
- **O-stack-09:** документация `font-display: swap` + WOFF2 в handoff

## 3. Архитектура — 6 фаз / 6 коммитов

```
[A. DB]      → schema.sql v8.10 → v8.11 (RLS + 2 FK indices) + CHANGELOG_schema
       ↓
[B. Backend] → app/ register middleware + trait + test fix
       ↓
[C. Configs] → 6 config files (npm/lychee/pa11y/composer/eslint/ci)
       ↓
[D. Docs (narrative)] → README + CLAUDE.md + Pravila + Tooling + cspell-words
       ↓
[E. Docs (handoff)]   → BRANDBOOK + DEVELOPER_HANDOFF (status mapping + axe doc + font-display)
       ↓
[F. Registry] → Открытые_вопросы (CTO + Histoire-Vite trigger)
```

**Зависимости:**

- B зависит от A (B упоминает CHANGELOG schema v8.11, который пишется в A).
- D зависит от A (D обновляет метрики schema в README/CLAUDE.md).
- E/F не зависят от A-D — могут идти параллельно после A-D, но для простоты выполняются последовательно.

Каждая фаза — отдельный git commit со своим осмысленным scope. После каждой фазы (где применимо) — verification (см. §6).

## 4. Детальный scope по фазам

### Фаза A — DB

**Файлы:** `db/schema.sql`, `db/CHANGELOG_schema.md`.

**Изменения:**

1. **P0-02:** добавить после `CREATE TABLE impersonation_tokens` (строка 510):

   ```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);
   ```

2. **O-perf-02:** добавить после индексов `failed_webhook_jobs`:

   ```sql
   CREATE INDEX idx_failed_webhook_jobs_log ON failed_webhook_jobs(webhook_log_id);
   ```

3. **O-perf-03:** добавить после индексов `rejected_deals_log`:

   ```sql
   CREATE INDEX idx_rejected_deals_log_webhook ON rejected_deals_log(webhook_log_id);
   ```

4. **CHANGELOG_schema.md:** добавить запись v8.10 → v8.11 со ссылкой на audit P0-02 + O-perf-02/03.

**Шапка schema.sql:** обновить версию v8.10 → v8.11, метрики 37 RLS → 38 RLS, 95 индексов → 97 индексов.

**Verification:** squawk на schema.sql; повторный grep на ENABLE RLS = 40, POLICY = 38; визуальная проверка diff.

### Фаза B — Backend

**Файлы:** `app/app/Http/Kernel.php`, `app/routes/web.php`, `app/app/Http/Requests/Auth/Concerns/HasPasswordRules.php` (создать), `app/app/Http/Requests/Auth/LoginRequest.php`, `app/app/Http/Requests/Auth/RegisterRequest.php`, `app/tests/Feature/AdminIncidentsIndexTest.php`.

**Изменения:**

1. **P0-01:** в `app/Http/Kernel.php` зарегистрировать alias:

   ```php
   protected $middlewareAliases = [
       // ... existing
       'tenant' => \App\Http\Middleware\SetTenantContext::class,
   ];
   ```

   В `routes/web.php` обернуть tenant-маршруты (Deal/Notification/Reminder/Report/Webhook) в группу `Route::middleware(['tenant'])->group(...)`. Сохранить тестовый flow с `tenant_id` query-param (через middleware читать оба источника: header + query).

   **Risk note:** middleware дополняет, не заменяет существующий MVP-flow. На prod-миграции (Б-1) `tenant_id`-param будет удалён, останется только middleware.

2. **O-refactor-03:** создать trait `HasPasswordRules`:

   ```php
   namespace App\Http\Requests\Auth\Concerns;

   trait HasPasswordRules
   {
       protected function passwordRules(): array
       {
           return ['required', 'string', 'min:8'];
       }
   }
   ```

   Подключить в `LoginRequest::rules()` и `RegisterRequest::rules()`.

3. **P2-01:** в `tests/Feature/AdminIncidentsIndexTest.php` заменить `bcrypt('test')` на `bcrypt('test1234')` (≥8 chars).

**Verification:** `cd app && composer test` — Pest 416/416 PASS. `composer stan` — 0 errors.

### Фаза C — Configs

**Файлы:** `package.json`, `.lychee.toml`, `pa11y.config.json`, `app/composer.json`, `app/eslint.config.js` (или новый `.eslintrc-vuetify-rules.js`), `.github/workflows/dependency-check.yml` (создать).

**Изменения:**

1. **P0-03:** в `package.json:13` заменить `/tmp/schema-formatted.sql` на `db/.schema-formatted.tmp.sql`. Добавить `db/.schema-formatted.tmp.sql` в `.gitignore`.

2. **P1-02:** в `.lychee.toml` добавить exclude для `web/v8/*.html` (root-relative ссылки концептов):

   ```toml
   exclude = [
       # ... existing
       "^/(login|register|legal|dashboard|deals|admin|reports|reminders|billing|impersonation|notifications)",
   ]
   ```

3. **P1-12:** в `pa11y.config.json` обновить пути с `web/01-login.html` (несуществующих) на `liderra_v8_handoff/concepts/v8_*.html` (фактические).

4. **P1-07:** в `app/composer.json` `scripts` добавить:

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

5. **O-refactor-05:** в `app/eslint.config.js` добавить правило `no-restricted-imports`:

   ```js
   {
       'no-restricted-imports': ['error', {
           paths: [{
               name: 'vuetify/components',
               message: 'Use auto-import via vite-plugin-vuetify (see vite.config.ts)',
           }],
       }],
   }
   ```

6. **O-stack-08:** создать `.github/workflows/dependency-check.yml`:

   ```yaml
   name: Dependency Check
   on:
     schedule: [{ cron: '0 9 * * 1' }]  # каждый понедельник 09:00 UTC
     workflow_dispatch:
   jobs:
     outdated:
       runs-on: ubuntu-latest
       steps:
         - uses: actions/checkout@v4
         - uses: actions/setup-node@v4
           with: { node-version: '20' }
         - run: npm install --ignore-scripts
         - run: npm outdated --json > outdated.json || true
         - name: Open issue if outdated
           if: ${{ hashFiles('outdated.json') != '' }}
           run: gh issue create --title "Weekly outdated check $(date)" --body "$(cat outdated.json)" --label dependencies
           env: { GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} }
   ```

**Verification:** `npm run format:sql:check` (на Windows) — больше не «system cannot find path»; `npm run links` — 0 errors на `docs/**` (web/v8/* исключён); ESLint smoke на правках Vue-файлов; pre-commit hooks PASS.

### Фаза D — Docs (narrative)

**Файлы:** `README.md`, `CLAUDE.md`, `docs/Pravila_raboty_Claude_v1_1.md`, `docs/Tooling_v8_3.md`, `cspell-words.txt`.

**Изменения:**

1. **P1-01:** в `README.md:83-90` синхронизировать с CLAUDE.md:
   - Tooling v1.0 → v1.10
   - Pravila v1.2 → v1.6
   - schema v8.5 → v8.11 (после Фазы A)
   - 54 таблицы / 91 индекс / 34 RLS → 56 таблиц / 97 индексов / 38 RLS (после Фазы A)

2. **P1-03:** в `CLAUDE.md:192` (и §3.3) обновить «Histoire 21/28» → «Histoire 21/43».

3. **P1-06:** в `docs/Pravila_raboty_Claude_v1_1.md:613` (приблизительно) добавить версию в ссылку: `[Plugin_stack_rules_v1.md](Plugin_stack_rules_v1.md) (v1.3)`.

4. **P1-08:** в `docs/Tooling_v8_3.md` Прил. Н §4.2 п.22 дополнить: `stylelint-config-standard ^40.0.0`.

5. **P2-02:** добавить `ребрендинга` в `cspell-words.txt`. (**Уже добавлено** во время self-review аудита 09.05.2026 — verify, не дублировать.)

6. **P2-03:** в шапку `CLAUDE.md` рядом с упоминанием «6 трений F–K» добавить ссылку: «(детали в [Plugin_stack_rules_v1.md История версий](../../Plugin_stack_rules_v1.md))».

**CLAUDE.md правки — через `claude-md-management:claude-md-improver`** (CLAUDE.md §5 п.10 — единственный путь). Pravila/Tooling — то же самое (плагин охватывает их по §0 priority chain).

**Verification:** `npm run check:docs` (markdownlint + cspell + lychee + a11y); pre-commit hooks PASS.

### Фаза E — Docs (handoff)

**Файлы:** `liderra_v8_handoff/docs/BRANDBOOK_v2.md`, `liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md`.

**Изменения:**

1. **P1-04:** в `DEVELOPER_HANDOFF.md:430+518` явно указать дату прогона axe-core (если есть в архиве) или пометить как «требует подтверждения после фикса pa11y.config (см. P1-12)». Не удалять заявление, но связать с воспроизводимым evidence.

2. **P1-05:** в `BRANDBOOK_v2.md` после §3.6 (14 OKLCH-статусов) добавить таблицу:

   | Slug (schema) | Имя BRANDBOOK | OKLCH | Статус-код |
   |---|---|---|---|
   | new | Новая | ... | ... |
   | viewed | Просмотрена | ... | ... |
   | ... (14 строк всего) | | | |

   Извлечь slug'и из `db/schema.sql:2172-2186` (INSERT lead_statuses), сопоставить с 14 цветами BRANDBOOK по hue-порядку. Если сопоставление неоднозначно — оставить TODO с явным вопросом дизайнеру.

3. **O-stack-09:** в `DEVELOPER_HANDOFF.md:250-258` (§4 Типографика) добавить раздел «Font loading strategy»:
   - Объяснить `&display=swap` (FOUT, fallback сразу).
   - WOFF2 формат по умолчанию из Google Fonts (лучше сжатие).
   - `<link rel="preconnect">` на `fonts.gstatic.com` для ускорения.

**Verification:** `npm run check:docs`; визуальная проверка таблицы.

**Note:** handoff правки — в репозитории `liderra_v8_handoff/`. Если этот dir не входит в наш git (sub-module / external) — Фаза E пропускается, фиксируется как «требует push в handoff-репо отдельно».

### Фаза F — Registry

**Файлы:** `docs/Открытые_вопросы_v8_3.md`.

**Изменения:**

1. **P1-10 → новый CTO-вопрос:**

   ```markdown
   ### CTO-XX (новый, открыт 09.05.2026)
   **Заголовок:** auth+tenant middleware на /api/deals на pre-prod миграции
   **Контекст:** MVP использует tenant_id query-param. На prod-миграции
   обязательно перейти на header `X-Tenant-Id` + middleware('tenant').
   **Trigger закрытия:** prod-миграция (после Б-1).
   **Связанные находки:** audit_2026-05-09.md P1-10.
   ```

2. **P1-11 → cross-link к Б-1:**

   ```markdown
   В разделе Б-1 (admin SSO) добавить упоминание /api/admin/* без auth = тот же блокер.
   ```

3. **P1-09 → новый OPEN-вопрос:**

   ```markdown
   ### OPEN-XX — Histoire ↔ Vite 8 миграционный долг
   **Контекст:** Histoire 1.0-beta.1 установлен через --legacy-peer-deps.
   **Trigger закрытия:** релиз Histoire с peerDep `vite ^8`.
   ```

**Verification:** `npm run links` — 0 errors на новые ссылки.

## 5. Definition of Done

Sprint 1 завершён, когда:

1. ✅ Все 22 правки из §2 + §4 применены, по одной фазе на коммит.
2. ✅ `cd app && composer test` — Pest 416/416 PASS (после Фазы B).
3. ✅ `cd app && composer stan` — 0 errors above baseline.
4. ✅ `cd app && npm run type-check` — 0 type errors.
5. ✅ `cd app && npm run test:vue` — Vitest 393/393 PASS.
6. ✅ `npm run check:docs` (markdownlint + cspell + lychee + a11y) — 0 errors.
7. ✅ Pre-commit hooks lefthook PASS на каждом коммите.
8. ✅ schema метрики обновлены до v8.11 (38 RLS / 97 индексов).
9. ✅ Git log: 6 коммитов с осмысленными scope-сообщениями.
10. ✅ После Фазы F: 3 новые/обновлённые записи в `docs/Открытые_вопросы_v8_3.md`.

**Не входит в DoD:**

- Реализация O-* за пределами 6 заявленных.
- Прогон Pa11y на handoff концептах (требует браузер-сессии — следствие P1-12 fix, может быть pre-prod manual step).
- Спринт 2/3 (modernization, big refactors).

## 6. Бюджет

| Фаза | Wall-clock |
|---|---|
| A. DB | 15-20 мин |
| B. Backend | 30-40 мин |
| C. Configs | 30-40 мин |
| D. Docs (narrative) | 30-45 мин (через claude-md-management) |
| E. Docs (handoff) | 30-45 мин |
| F. Registry | 10-15 мин |
| **Итого** | **2.5-3.5 часа** |

## 7. Риски

- **Риск:** RLS-policy на impersonation_tokens может сломать существующие тесты, если они не используют `SET LOCAL app.current_tenant_id`. Митигация: после Фазы A прогнать Pest, если что-то падает — добавить `SET LOCAL` в test setup.
- **Риск:** SetTenantContext middleware регистрация может перехватить тестовые маршруты с tenant_id query-param. Митигация: в Фазе B middleware читает оба источника (header + query) до prod-миграции.
- **Риск:** Фаза E (handoff) может попасть в внешний репозиторий — если так, фаза формально пропускается с пометкой «требует ручной push».
- **Риск:** Фаза D правки CLAUDE.md/Pravila обязательно через `claude-md-management:claude-md-improver` (CLAUDE.md §5 п.10). Прямые Edit/Write этих файлов без skill = нарушение.

## 8. Плагины и MCP

| Плагин / Skill | Применение |
|---|---|
| `superpowers:writing-plans` | Сразу после approve этого spec'а (для implementation plan) |
| `superpowers:subagent-driven-development` | Исполнение plan'а в этой же сессии |
| `superpowers:verification-before-completion` | После каждой фазы перед коммитом |
| `claude-md-management:claude-md-improver` | **Обязательно** для Фазы D правок CLAUDE.md/Pravila/Tooling (CLAUDE.md §5 п.10) |
| `laravel-boost` MCP | По необходимости в Фазе B (Eloquent, маршруты) |
| `github` MCP | По необходимости в Фазе C для GitHub Actions workflow |
| Frontend Design plugin | НЕ призывается (R10: аудит-фиксы ≠ дизайн) |
| simplify / security-review / review / init / ui-ux-pro-max | НЕ призываются (R10: только по явному /команде) |

## 9. Не входит в этот spec

- Спринт 2 «Modernization» (Pest 4 browser/mutation, Vue 3.5 фичи, Vuetify 3.12 типизированные слоты, Laravel 13 lazy-loading) — отдельный spec→plan→execute.
- Спринт 3 «Big refactors» (DealController split, AuthController split, 12 Vue-компонентов >300 строк, OFFSET → keyset, export streaming, CLAUDE.md §0 reorg) — отдельный spec→plan→execute.
- Pa11y prod-режим на handoff — manual user step после Фазы C (P1-12 fix).
- Регистрация Frontend Design plugin в `~/.claude/settings.json` (O-stack-06) — manual user step, файл вне git.
