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

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Закрыть 22 фикса аудита (3 P0 + 10 P1 + 3 P2 + 6 low-risk O-\*) + 3 записи в реестр, разбитых на 6 атомарных коммитов по доменам.

**Architecture:** 6 фаз / 6 коммитов. Каждая фаза — изолированный scope, заканчивается verification (соответствующие линтеры/тесты) и коммитом. Зависимости: B и D зависят от A (метрики schema + CHANGELOG ссылка). Phase D правки CLAUDE.md/Pravila/Tooling — обязательно через `claude-md-management:claude-md-improver` skill (CLAUDE.md §5 п.10).

**Tech Stack:** PostgreSQL 16, Laravel 13.7, PHP 8.3, Pest 4, Larastan, Vue 3 + Vuetify 3, ESLint flat-config, Pinia, Axios, lefthook, GitHub Actions, npm scripts, lychee, cspell, markdownlint, pa11y. Платформа: Windows Server 2022, PowerShell + Bash.

**Spec:** [docs/superpowers/specs/2026-05-09-sprint1-hygiene-design.md](../specs/2026-05-09-sprint1-hygiene-design.md) v1.0, commit `08bbe4d`.

---

## File Structure

| Файл | Действие | Что меняется |
|---|---|---|
| `db/schema.sql` | Modify | RLS на impersonation_tokens (после строки 540), 2 индекса (после 1521 и 1537), bump версии в шапке v8.10→v8.11, обновить метрики |
| `db/CHANGELOG_schema.md` | Modify | Новая запись «v8.11 от 09.05.2026» в начале (reverse chronological) |
| `app/routes/web.php` | Modify | Добавить `'tenant'` к существующим auth:sanctum group'ам (строки 44, 52, 63) |
| `app/app/Http/Requests/Auth/Concerns/HasPasswordRules.php` | Create | Trait c rules + messages для password |
| `app/app/Http/Requests/Auth/LoginRequest.php` | Modify | Подключить trait, заменить inline rules |
| `app/app/Http/Requests/Auth/RegisterRequest.php` | Modify | То же |
| `app/tests/Feature/AdminIncidentsIndexTest.php` | Modify | bcrypt('test') → bcrypt('test1234') |
| `package.json` | Modify | format:sql:check Windows-совместимый путь |
| `.gitignore` | Modify | Добавить `db/.schema-formatted.tmp.sql` |
| `.lychee.toml` | Modify | exclude root-relative ссылок web/v8 |
| `pa11y.config.json` | Modify | Пути к liderra_v8_handoff/concepts/v8_*.html |
| `app/composer.json` | Modify | Скрипт `audit-offline` |
| `app/eslint.config.js` | Modify | Правило `no-restricted-imports` против vuetify/components |
| `.github/workflows/dependency-check.yml` | Create | Еженедельный npm outdated workflow |
| `README.md` | Modify | Versions sync (Tooling v1.10, Pravila v1.6, schema v8.11) |
| `CLAUDE.md` | Modify (через claude-md-management) | Histoire 21/43, F-K ссылка |
| `docs/Pravila_raboty_Claude_v1_1.md` | Modify (через claude-md-management) | §13.9 cross-ref version |
| `docs/Tooling_v8_3.md` | Modify (через claude-md-management) | stylelint-config-standard ^40.0.0 в §4.2 п.22 |
| `cspell-words.txt` | Verify | `ребрендинга` уже добавлено в self-review аудита, не дублировать |
| `liderra_v8_handoff/docs/BRANDBOOK_v2.md` | Modify | Таблица 14 status-slug mapping |
| `liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md` | Modify | Axe-claim documentation + font-display strategy |
| `docs/Открытые_вопросы_v8_3.md` | Modify | Новый CTO для P1-10, cross-link для P1-11, новый OPEN для P1-09 |

---

## Phase A. DB — schema.sql v8.10 → v8.11

### Task A1: Добавить RLS на impersonation_tokens

**Files:**

- Modify: `db/schema.sql:540` (после `idx_imp_tokens_admin`, до forward FK ALTER)

- [ ] **Step A1.1: Прочитать существующий блок RLS политик в schema.sql, чтобы скопировать стиль**

```bash
sed -n '2390,2410p' db/schema.sql
```

Expected: видим существующие `CREATE POLICY tenant_isolation ON ... USING (tenant_id = current_setting('app.current_tenant_id')::bigint);`

- [ ] **Step A1.2: Добавить ALTER TABLE + CREATE POLICY для impersonation_tokens**

В `db/schema.sql` после строки 540 (`CREATE INDEX idx_imp_tokens_admin ...`), перед `-- Forward FK на impersonation_tokens`, вставить:

```sql

-- v8.11 (audit P0-02): RLS-isolation между тенантами для одноразовых impersonation-токенов
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);

```

- [ ] **Step A1.3: Verify добавление**

```bash
grep -n "ENABLE ROW LEVEL SECURITY" db/schema.sql | wc -l
grep -cE "^CREATE POLICY" db/schema.sql
```

Expected: ENABLE RLS = **40** (было 39, +1), CREATE POLICY = **38** (было 37, +1).

### Task A2: Добавить index на failed_webhook_jobs.webhook_log_id

**Files:**

- Modify: `db/schema.sql:1521` (после `idx_failed_webhook_unresolved`)

- [ ] **Step A2.1: Добавить индекс**

После строки 1521 (`CREATE INDEX idx_failed_webhook_unresolved ON failed_webhook_jobs(failed_at DESC) WHERE resolved_at IS NULL;`) вставить:

```sql
CREATE INDEX idx_failed_webhook_jobs_log ON failed_webhook_jobs(webhook_log_id);  -- v8.11 (audit O-perf-02)
```

### Task A3: Добавить index на rejected_deals_log.webhook_log_id

**Files:**

- Modify: `db/schema.sql:1537` (после `idx_rejected_tenant_created`)

- [ ] **Step A3.1: Добавить индекс**

После строки 1537 (`CREATE INDEX idx_rejected_tenant_created ON rejected_deals_log(tenant_id, created_at DESC);`) вставить:

```sql
CREATE INDEX idx_rejected_deals_log_webhook ON rejected_deals_log(webhook_log_id);  -- v8.11 (audit O-perf-03)
```

### Task A4: Bump версии и метрик в шапке schema.sql

**Files:**

- Modify: `db/schema.sql:1-30` (шапка)

- [ ] **Step A4.1: Найти шапку и обновить**

```bash
sed -n '1,30p' db/schema.sql
```

Найти строку с версией `v8.10` и метриками. Изменить:

- `v8.10` → `v8.11`
- Дату на `09.05.2026` (если уже не она)
- Метрики: `37 RLS` → `38 RLS`, `95 индексов` → `97 индексов`

### Task A5: Запись в CHANGELOG_schema.md

**Files:**

- Modify: `db/CHANGELOG_schema.md` (добавить новую запись в самом верху, после введения)

- [ ] **Step A5.1: Найти место для вставки**

```bash
head -20 db/CHANGELOG_schema.md
```

Expected: видим intro + первую существующую запись (например, `## v8.10 от ...`).

- [ ] **Step A5.2: Вставить новую запись «v8.11 от 09.05.2026» перед v8.10**

Шаблон:

```markdown
## v8.11 от 09.05.2026 — hygiene-фиксы аудита

**Источник:** [docs/audit_2026-05-09.md](../docs/audit_2026-05-09.md) (commit `b6ae8dd`).

### Изменения

1. **P0-02:** Добавлены `ALTER TABLE impersonation_tokens ENABLE ROW LEVEL SECURITY` и `CREATE POLICY tenant_isolation ON impersonation_tokens` (схема ~ строка 540).
2. **O-perf-02:** Добавлен индекс `idx_failed_webhook_jobs_log` на `failed_webhook_jobs(webhook_log_id)` (строка 1521).
3. **O-perf-03:** Добавлен индекс `idx_rejected_deals_log_webhook` на `rejected_deals_log(webhook_log_id)` (строка 1537).

### Метрики (после v8.11)

- 56 базовых таблиц + 12 партиций (без изменений, 68 CREATE TABLE)
- **97 индексов** (было 95, +2 = idx_failed_webhook_jobs_log, idx_rejected_deals_log_webhook)
- **38 RLS-политик** (было 37, +1 = tenant_isolation на impersonation_tokens)
- 5 функций, 13 триггеров (без изменений)

### Применение

```bash
cd app && php artisan migrate:fresh
```

Для существующего tenant'а с данными миграция через `ALTER TABLE` + `CREATE POLICY` + `CREATE INDEX CONCURRENTLY` (3 раздельных DDL без блокировок).

```

### Task A6: Verification схемы

- [ ] **Step A6.1: squawk smoke**

```bash
npm run lint:sql
```

Expected: `Found 0 issues in 1 file 🎉`. Если не 0 — разобраться, не коммитить.

- [ ] **Step A6.2: Метрики**

```bash
echo "ENABLE RLS: $(grep -cE 'ENABLE ROW LEVEL SECURITY' db/schema.sql)"
echo "CREATE POLICY: $(grep -cE '^CREATE POLICY' db/schema.sql)"
echo "CREATE INDEX (incl UNIQUE): $(grep -cE '^CREATE\s+(UNIQUE\s+)?INDEX' db/schema.sql)"
```

Expected: `ENABLE RLS: 40`, `CREATE POLICY: 38`, `CREATE INDEX: 97`.

Note: ENABLE RLS = 40 vs POLICY = 38 — gap 2 остаётся (одна повторно выявленная косвенно — отдельная задача, не Sprint 1).

### Task A7: Commit Phase A

- [ ] **Step A7.1: Stage и commit**

```bash
git add db/schema.sql db/CHANGELOG_schema.md
git commit -m "$(cat <<'EOF'
fix(db): RLS на impersonation_tokens + 2 missing FK indices (audit P0-02 + O-perf-02/03)

schema.sql v8.10 → v8.11. Закрытие аудита 2026-05-09 (b6ae8dd):
- P0-02: ALTER TABLE impersonation_tokens ENABLE ROW LEVEL SECURITY
  + CREATE POLICY tenant_isolation. Закрывает RLS-gap (39 vs 37 → 40 vs 38).
- O-perf-02: CREATE INDEX idx_failed_webhook_jobs_log на webhook_log_id.
- O-perf-03: CREATE INDEX idx_rejected_deals_log_webhook на webhook_log_id.

Метрики v8.11: 56 базовых + 12 партиций / 97 индексов / 38 RLS / 5 функций / 13 триггеров.
CHANGELOG_schema.md обновлён.
squawk: 0 issues.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

Expected: pre-commit hook PASS (gitleaks, markdownlint на CHANGELOG, cspell, squawk).

---

## Phase B. Backend

### Task B1: Применить middleware 'tenant' к существующим auth:sanctum-группам

**Files:**

- Modify: `app/routes/web.php:44`, `:52`, `:63`

**Note:** alias `'tenant'` **уже зарегистрирован** в `app/bootstrap/app.php:17`. Регистрировать заново не нужно. Задача — применить middleware к группам.

- [ ] **Step B1.1: Прочитать текущее состояние routes/web.php**

```bash
sed -n '44p;52p;63p' app/routes/web.php
```

Expected:

```text
Route::middleware('auth:sanctum')->prefix('/api/notifications')->group(function () {
Route::middleware('auth:sanctum')->prefix('/api/reminders')->group(function () {
Route::middleware('auth:sanctum')->prefix('/api/reports/jobs')->group(function () {
```

- [ ] **Step B1.2: Заменить middleware('auth:sanctum') на middleware(['auth:sanctum', 'tenant'])**

Через Edit tool, для каждой из 3 строк (44, 52, 63):

```text
Route::middleware('auth:sanctum')->prefix(...)
```

→

```text
Route::middleware(['auth:sanctum', 'tenant'])->prefix(...)
```

**НЕ трогаем** `/api/deals` (P1-10) и `/api/admin` (P1-11) — они в реестр, не правки в Sprint 1.

- [ ] **Step B1.3: Verify изменений**

```bash
grep -n "middleware(\['auth:sanctum', 'tenant'\])" app/routes/web.php | wc -l
```

Expected: `3`.

### Task B2: Создать HasPasswordRules trait

**Files:**

- Create: `app/app/Http/Requests/Auth/Concerns/HasPasswordRules.php`

- [ ] **Step B2.1: Создать каталог Concerns**

```bash
mkdir -p app/app/Http/Requests/Auth/Concerns
```

- [ ] **Step B2.2: Записать trait**

`app/app/Http/Requests/Auth/Concerns/HasPasswordRules.php`:

```php
<?php

declare(strict_types=1);

namespace App\Http\Requests\Auth\Concerns;

trait HasPasswordRules
{
    /**
     * Правила валидации поля password.
     * Используется в LoginRequest и RegisterRequest для DRY.
     *
     * @return array<int, string>
     */
    protected function passwordRules(): array
    {
        return ['required', 'string', 'min:8', 'max:255'];
    }

    /**
     * Сообщения об ошибках для password.
     *
     * @return array<string, string>
     */
    protected function passwordMessages(): array
    {
        return [
            'password.required' => 'Укажите пароль.',
            'password.min' => 'Пароль должен быть не короче 8 символов.',
        ];
    }
}
```

### Task B3: Подключить trait в LoginRequest

**Files:**

- Modify: `app/app/Http/Requests/Auth/LoginRequest.php`

- [ ] **Step B3.1: Добавить use trait + заменить rules['password'] и сообщения**

Через Edit tool заменить тело класса. Старое:

```php
class LoginRequest extends FormRequest
{
    /** @return array<string, mixed> */
    public function rules(): array
    {
        return [
            'email' => ['required', 'string', 'email', 'max:255'],
            'password' => ['required', 'string', 'min:8', 'max:255'],
            'remember' => ['sometimes', 'boolean'],
        ];
    }

    /** @return array<string, string> */
    public function messages(): array
    {
        return [
            'email.required' => 'Укажите email.',
            'email.email' => 'Email указан некорректно.',
            'password.required' => 'Укажите пароль.',
            'password.min' => 'Пароль должен быть не короче 8 символов.',
        ];
    }
}
```

Новое:

```php
class LoginRequest extends FormRequest
{
    use \App\Http\Requests\Auth\Concerns\HasPasswordRules;

    /** @return array<string, mixed> */
    public function rules(): array
    {
        return [
            'email' => ['required', 'string', 'email', 'max:255'],
            'password' => $this->passwordRules(),
            'remember' => ['sometimes', 'boolean'],
        ];
    }

    /** @return array<string, string> */
    public function messages(): array
    {
        return array_merge($this->passwordMessages(), [
            'email.required' => 'Укажите email.',
            'email.email' => 'Email указан некорректно.',
        ]);
    }
}
```

### Task B4: Подключить trait в RegisterRequest

**Files:**

- Modify: `app/app/Http/Requests/Auth/RegisterRequest.php`

- [ ] **Step B4.1: Аналогично LoginRequest**

Старое:

```php
class RegisterRequest extends FormRequest
{
    /** @return array<string, mixed> */
    public function rules(): array
    {
        return [
            'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users', 'email')],
            'password' => ['required', 'string', 'min:8', 'max:255'],
            'accept_offer' => ['required', 'accepted'],
            'accept_pdn' => ['required', 'accepted'],
        ];
    }

    /** @return array<string, string> */
    public function messages(): array
    {
        return [
            'email.required' => 'Укажите email.',
            'email.email' => 'Email указан некорректно.',
            'email.unique' => 'Аккаунт с таким email уже существует.',
            'password.min' => 'Пароль должен быть не короче 8 символов.',
            'accept_offer.accepted' => 'Необходимо принять оферту.',
            'accept_pdn.accepted' => 'Необходимо согласие на обработку персональных данных.',
        ];
    }
```

Новое:

```php
class RegisterRequest extends FormRequest
{
    use \App\Http\Requests\Auth\Concerns\HasPasswordRules;

    /** @return array<string, mixed> */
    public function rules(): array
    {
        return [
            'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users', 'email')],
            'password' => $this->passwordRules(),
            'accept_offer' => ['required', 'accepted'],
            'accept_pdn' => ['required', 'accepted'],
        ];
    }

    /** @return array<string, string> */
    public function messages(): array
    {
        return array_merge($this->passwordMessages(), [
            'email.required' => 'Укажите email.',
            'email.email' => 'Email указан некорректно.',
            'email.unique' => 'Аккаунт с таким email уже существует.',
            'accept_offer.accepted' => 'Необходимо принять оферту.',
            'accept_pdn.accepted' => 'Необходимо согласие на обработку персональных данных.',
        ]);
    }
```

### Task B5: Фикс test password ≥8

**Files:**

- Modify: `app/tests/Feature/AdminIncidentsIndexTest.php`

- [ ] **Step B5.1: Найти и заменить bcrypt('test')**

```bash
grep -n "bcrypt('test')" app/tests/Feature/AdminIncidentsIndexTest.php
```

Через Edit заменить `bcrypt('test')` → `bcrypt('test1234')`.

### Task B6: Verification Phase B

- [ ] **Step B6.1: Pest полный прогон**

```bash
cd app && composer test
```

Expected: `Tests: 416 passed (1388 assertions)` или больше. **Если меньше тестов проходит — STOP**, разобраться (вероятная причина: `tenant`-middleware блокирует тесты на /api/notifications/reminders/reports без `SET LOCAL app.current_tenant_id`).

**Расширенная проверка:** если тесты падают из-за middleware — добавить в test setup (например, в `tests/TestCase.php`) фикстуру `DB::statement("SET LOCAL app.current_tenant_id = " . $this->tenant->id);` для аутентифицированных тестов.

- [ ] **Step B6.2: Larastan**

```bash
cd app && composer stan
```

Expected: `0 errors`.

- [ ] **Step B6.3: Pint format check**

```bash
cd app && composer pint:test
```

Expected: PASSED. Если что-то не отформатировано — `composer pint` для авто-fix.

### Task B7: Commit Phase B

- [ ] **Step B7.1: Stage и commit**

```bash
git add 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
git commit -m "$(cat <<'EOF'
fix(backend): tenant middleware на auth-routes + HasPasswordRules trait + test password (audit P0-01 + O-refactor-03 + P2-01)

Закрытие аудита 2026-05-09 (b6ae8dd):
- P0-01: применён 'tenant' middleware (alias уже в bootstrap/app.php:17) к 3 auth:sanctum-группам:
  /api/notifications, /api/reminders, /api/reports/jobs (web.php:44/52/63).
  /api/deals и /api/admin/* остаются без auth (P1-10/Б-1) — в реестр Спринта 1 Phase F.
- O-refactor-03: HasPasswordRules trait извлекает rules + messages, подключён в Login/Register.
- P2-01: bcrypt('test') → bcrypt('test1234') в AdminIncidentsIndexTest.

Pest: 416/416 PASS.
Larastan: 0 errors.
Pint: clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Phase C. Configs

### Task C1: format:sql:check Windows fix

**Files:**

- Modify: `package.json:13`
- Modify: `.gitignore`

- [ ] **Step C1.1: Заменить Unix `/tmp/` путь**

В `package.json` строка `"format:sql:check"`:

Старое:

```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\""
```

Новое:

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

- [ ] **Step C1.2: Добавить tmp-файл в .gitignore**

В `.gitignore` добавить (если ещё нет):

```text
db/.schema-formatted.tmp.sql
```

- [ ] **Step C1.3: Verify работа на этой Windows-машине**

```bash
npm run format:sql:check
```

Expected: больше нет `The system cannot find the path specified`. Вместо этого либо успех, либо «pgFormatter would reformat» с реальной diff'ой.

### Task C2: .lychee.toml exclude web/v8

**Files:**

- Modify: `.lychee.toml` (раздел `exclude`)

- [ ] **Step C2.1: Прочитать текущий exclude**

```bash
grep -A 30 "^exclude = \[" .lychee.toml | head -35
```

- [ ] **Step C2.2: Добавить regex для root-relative ссылок web/v8**

В блок `exclude` добавить (перед закрывающей `]`):

```toml
  # web/v8/*.html — статические концепты, root-relative ссылки на будущие маршруты Vue
  "^/(login|register|legal|dashboard|deals|admin|reports|reminders|billing|impersonation|notifications)(/|$|\\?)",
```

- [ ] **Step C2.3: Verify уменьшения числа errors**

```bash
npm run links 2>&1 | grep "Errors"
```

Expected: число errors **меньше 19** (близко к 0; могут остаться внешние URL, не связанные с web/v8).

### Task C3: pa11y.config.json правильные пути

**Files:**

- Modify: `pa11y.config.json`

- [ ] **Step C3.1: Прочитать текущие urls**

```bash
cat pa11y.config.json
```

- [ ] **Step C3.2: Проверить какие концепты реально существуют**

```bash
ls liderra_v8_handoff/concepts/v8_*.html 2>/dev/null | head -5
```

- [ ] **Step C3.3: Обновить urls**

В `pa11y.config.json` заменить пути типа `web/01-login.html` на актуальные `liderra_v8_handoff/concepts/v8_login.html` (имя файла подкорректировать под фактическую structure из шага C3.2). Если есть ровно 13 концептов — всех 13 включить. Если в config'е сейчас только 3 url'а (sample) — оставить 3 sample, но из реальных путей.

### Task C4: composer.json audit-offline скрипт

**Files:**

- Modify: `app/composer.json` (раздел `scripts`)

- [ ] **Step C4.1: Добавить скрипт**

В `app/composer.json` `scripts`-объект (после существующего `stan` или рядом):

```json
"audit-offline": "@composer audit --locked",
```

Note: флага `--no-network` нет в современных Composer 2.x; `--locked` использует только локальный composer.lock без обращения к packagist API.

- [ ] **Step C4.2: Verify**

```bash
cd app && composer audit-offline
```

Expected: вывод без curl-таймаутов; либо «No security vulnerability advisories found», либо реальный список уязвимостей.

### Task C5: ESLint no-restricted-imports vuetify rule

**Files:**

- Modify: `app/eslint.config.js`

- [ ] **Step C5.1: Прочитать текущий конфиг**

```bash
cat app/eslint.config.js
```

- [ ] **Step C5.2: Расширить блок `rules`**

Найти в `eslint.config.js` секцию:

```js
{
    rules: {
        'vue/multi-word-component-names': 'off', // допускаем AppShell.vue
    },
},
```

Заменить на:

```js
{
    rules: {
        'vue/multi-word-component-names': 'off', // допускаем AppShell.vue
        'no-restricted-imports': ['error', {
            paths: [{
                name: 'vuetify/components',
                message: 'Используй auto-import через vite-plugin-vuetify (vite.config.ts). Явные импорты дублируют auto-injected.',
            }],
        }],
    },
},
```

- [ ] **Step C5.3: Verify rule срабатывает**

```bash
cd app && npm run lint:vue
```

Expected: 0 errors (никаких явных импортов из vuetify/components в актуальном коде нет — D5-агент в аудите подтвердил).

### Task C6: GitHub Actions weekly dependency-check workflow

**Files:**

- Create: `.github/workflows/dependency-check.yml`

- [ ] **Step C6.1: Создать каталог если нет**

```bash
mkdir -p .github/workflows
ls .github/workflows/
```

- [ ] **Step C6.2: Записать workflow**

`.github/workflows/dependency-check.yml`:

```yaml
name: Dependency Check

on:
  schedule:
    - cron: '0 9 * * 1'  # каждый понедельник 09:00 UTC
  workflow_dispatch:

permissions:
  contents: read
  issues: write

jobs:
  npm-outdated:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install root deps
        run: npm install --ignore-scripts

      - name: Check outdated (root)
        id: root_outdated
        run: |
          npm outdated --json > root-outdated.json || true
          if [ "$(cat root-outdated.json)" != "{}" ]; then
            echo "has_updates=true" >> "$GITHUB_OUTPUT"
          fi

      - name: Install app deps
        working-directory: app
        run: npm install --ignore-scripts

      - name: Check outdated (app)
        id: app_outdated
        working-directory: app
        run: |
          npm outdated --json > app-outdated.json || true
          if [ "$(cat app-outdated.json)" != "{}" ]; then
            echo "has_updates=true" >> "$GITHUB_OUTPUT"
          fi

      - name: Open issue if outdated
        if: steps.root_outdated.outputs.has_updates == 'true' || steps.app_outdated.outputs.has_updates == 'true'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          {
            echo "## Weekly outdated dependencies — $(date -u +"%Y-%m-%d")"
            echo
            echo "### Root (package.json)"
            echo '```json'
            cat root-outdated.json
            echo '```'
            echo
            echo "### App (app/package.json)"
            echo '```json'
            cat app/app-outdated.json
            echo '```'
          } > issue-body.md
          gh issue create --title "Weekly outdated check $(date -u +"%Y-%m-%d")" --body-file issue-body.md --label dependencies
```

### Task C7: Verification Phase C

- [ ] **Step C7.1: Полный docs check**

```bash
npm run check:docs
```

Expected: markdownlint 0 errors, cspell 0 issues, lychee 0 errors (web/v8 теперь исключены), pa11y — может быть 0/N если сервер dev не запущен; не блокирует.

- [ ] **Step C7.2: ESLint smoke**

```bash
cd app && npm run lint:vue
```

Expected: 0 errors.

- [ ] **Step C7.3: format:sql:check на Windows**

```bash
npm run format:sql:check
```

Expected: либо «pgFormatter would reformat» с реальным diff'ом, либо успех. **НЕ должно быть** «The system cannot find the path specified».

### Task C8: Commit Phase C

- [ ] **Step C8.1: Stage и commit**

```bash
git add package.json .gitignore .lychee.toml pa11y.config.json \
        app/composer.json app/eslint.config.js \
        .github/workflows/dependency-check.yml
git commit -m "$(cat <<'EOF'
fix(configs): Windows-fix format:sql:check + lychee/pa11y/composer/ESLint hygiene + npm-outdated CI (audit P0-03 + 5 P1/O)

Закрытие аудита 2026-05-09 (b6ae8dd):
- P0-03: format:sql:check заменён /tmp/ на db/.schema-formatted.tmp.sql (Windows-совместимо).
- P1-02: .lychee.toml exclude root-relative для web/v8/*.html (19 false-positive errors → 0).
- P1-12: pa11y.config.json пути обновлены на liderra_v8_handoff/concepts/v8_*.html.
- P1-07: composer audit-offline скрипт (composer audit --locked) для офлайн-режима.
- O-refactor-05: ESLint no-restricted-imports запрещает явный import из 'vuetify/components'.
- O-stack-08: .github/workflows/dependency-check.yml — еженедельный (Mon 09:00 UTC) npm outdated с авто-issue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Phase D. Docs (narrative)

**ВАЖНО:** все правки `CLAUDE.md`, `Pravila`, `Tooling` — обязательно через `claude-md-management:claude-md-improver` skill (CLAUDE.md §5 п.10). README.md и cspell-words.txt — обычные правки.

### Task D1: README.md versions sync

**Files:**

- Modify: `README.md` (раздел «Источник истины» / «Документы», ~строки 83-90)

- [ ] **Step D1.1: Прочитать существующий блок**

```bash
sed -n '75,100p' README.md
```

- [ ] **Step D1.2: Обновить через Edit tool**

Найти упоминания и заменить:

- `Tooling v1.0` → `Tooling Прил. Н v1.10`
- `Pravila v1.2` → `Pravila v1.6`
- `schema v8.5` → `schema v8.11`
- `54 таблиц / 91 индекс / 34 RLS` → `56 таблиц / 12 партиций / 97 индексов / 38 RLS / 5 функций / 13 триггеров`

Точные имена / форматировка зависят от текущего состояния README.md (которое прочитано на шаге D1.1).

- [ ] **Step D1.3: Verify**

```bash
grep -E "v1\.10|v1\.6|v8\.11|56 таблиц|97 индекс|38 RLS" README.md | head -5
```

Expected: ≥3 строки matched.

### Task D2: Invoke claude-md-management:claude-md-improver для CLAUDE.md/Pravila/Tooling

**Files (правки):**

- Modify: `CLAUDE.md`
- Modify: `docs/Pravila_raboty_Claude_v1_1.md`
- Modify: `docs/Tooling_v8_3.md`

- [ ] **Step D2.1: Активировать skill `claude-md-management:claude-md-improver`**

Вызов skill через Skill tool. На входе skill'у — следующие задачи (передать в args одним блоком):

```text
Targeted updates по итогам аудита 2026-05-09 (commit b6ae8dd, см. docs/audit_2026-05-09.md):

1. CLAUDE.md (P1-03): обновить метрику Histoire в §0 строке про «Открытые вопросы» и в §3.3 #24:
   - было: «21 story / 28 variants»
   - стало: «21 story / 43 variants»

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

3. CLAUDE.md (post P0-02): обновить §2 строку про метрики PG schema:
   - было: «56 таблиц + 12 партиций, 95 индексов, 37 RLS-политик, 4 роли БД, 13 триггеров, 5 функций — schema v8.10»
   - стало: «56 таблиц + 12 партиций, 97 индексов, 38 RLS-политик, 4 роли БД, 13 триггеров, 5 функций — schema v8.11»
   Также обновить §0 ссылку на db/schema.sql: v8.10 → v8.11.

4. Pravila §13.9 (P1-06): добавить версию (v1.3) в ссылку на Plugin_stack_rules_v1:
   - было: «Нарушение Правила 10 [Plugin_stack_rules_v1.md](Plugin_stack_rules_v1.md) (введено в PSR v1.2)»
   - стало: «Нарушение Правила 10 [Plugin_stack_rules_v1.md](Plugin_stack_rules_v1.md) (v1.3)»
   Точная локация — найти grep'ом по «Plugin_stack_rules_v1.md» в Pravila_raboty_Claude_v1_1.md.

5. Tooling §4.2 п.22 (P1-08): дополнить пакет stylelint-config-standard версией:
   - было (Прил. Н §4.2 п.22): «Stylelint v17.x» (приблизительно)
   - стало: добавить отдельную строку про `stylelint-config-standard ^40.0.0`
   Точная локация — Прил. Н, раздел про CSS/Stylelint.

Все 5 правок — targeted updates, без структурных изменений. После правок — bump CLAUDE.md версии 1.81 → 1.82, Pravila v1.6 → v1.7, Tooling Прил. Н v1.10 → v1.11. История версий в каждом файле — обновить.

Не делать ничего за пределами этих 5 пунктов. Не добавлять новых section'ов. Не менять формат.
```

- [ ] **Step D2.2: Дождаться завершения skill'а и verify правок**

```bash
git status --short
```

Expected: 3 modified files (CLAUDE.md, Pravila, Tooling). Если skill также правит другие — STOP, разобраться.

- [ ] **Step D2.3: Spot-check отдельные правки**

```bash
grep -n "21 story / 43 variants" CLAUDE.md
grep -n "v1\.3" docs/Pravila_raboty_Claude_v1_1.md | head -5
grep -n "stylelint-config-standard" docs/Tooling_v8_3.md
grep -n "v8.11" CLAUDE.md
grep -n "97 индекс" CLAUDE.md
grep -n "38 RLS" CLAUDE.md
```

Expected: каждая команда даёт ≥1 результат.

### Task D3: cspell-words.txt verify

**Files:**

- Verify only: `cspell-words.txt`

- [ ] **Step D3.1: Проверить, что `ребрендинга` уже в словаре**

```bash
grep "^ребрендинга$" cspell-words.txt
```

Expected: 1 строка matched. Если нет — добавить:

```bash
echo "ребрендинга" >> cspell-words.txt
```

(P2-02 был добавлен во время self-review аудита 09.05.2026, должен быть в commit `b6ae8dd`.)

### Task D4: Verification Phase D

- [ ] **Step D4.1: Полный docs check**

```bash
npm run check:docs
```

Expected: markdownlint 0, cspell 0, lychee 0 (после Phase C exclude применён), pa11y — informative.

### Task D5: Commit Phase D

- [ ] **Step D5.1: Stage и commit**

```bash
git add README.md CLAUDE.md docs/Pravila_raboty_Claude_v1_1.md docs/Tooling_v8_3.md cspell-words.txt
git commit -m "$(cat <<'EOF'
docs(narrative): sync versions + Histoire 21/43 + cross-refs (audit P1-01/03/06/08 + P2-02/03)

Закрытие аудита 2026-05-09 (b6ae8dd) для narrative-блока:
- P1-01: README.md обновлён (Tooling v1.10, Pravila v1.6, schema v8.11 / 56 / 97 / 38).
- P1-03: CLAUDE.md Histoire 21/28 → 21/43 (§0 + §3.3).
- P1-06: Pravila §13.9 cross-ref на Plugin_stack_rules_v1 теперь с (v1.3).
- P1-08: Tooling Прил. Н дополнен stylelint-config-standard ^40.0.0.
- P2-02: cspell-words.txt уже содержит «ребрендинга» (verified, добавлено в self-review аудита).
- P2-03: CLAUDE.md F-K линка на Plugin_stack_rules_v1#история-версий.
- post-A: метрики schema в CLAUDE.md обновлены до v8.11 (97 индексов, 38 RLS).

Все правки CLAUDE.md/Pravila/Tooling — через claude-md-management:claude-md-improver
(CLAUDE.md §5 п.10). README.md — обычная правка.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Phase E. Docs (handoff)

**Pre-check:** проверить, что `liderra_v8_handoff/` входит в наш git (а не submodule/external).

```bash
git ls-files liderra_v8_handoff/docs/BRANDBOOK_v2.md
```

Если результат пустой (файл не tracked в текущем repo) — Phase E **пропустить целиком**, отметить в финальном отчёте «требует push в handoff-репо отдельно». Если файл tracked — продолжать.

### Task E1: BRANDBOOK_v2 — таблица 14 status-slug mapping (P1-05)

**Files:**

- Modify: `liderra_v8_handoff/docs/BRANDBOOK_v2.md` (после §3.6 — 14 OKLCH-статусов)

- [ ] **Step E1.1: Извлечь slug'и из schema.sql**

```bash
sed -n '2170,2200p' db/schema.sql
```

Expected: видим `INSERT INTO lead_statuses ... ('new', ...), ('viewed', ...), ...` с 14 строками.

- [ ] **Step E1.2: Извлечь имена/цвета из BRANDBOOK_v2**

```bash
sed -n '160,200p' liderra_v8_handoff/docs/BRANDBOOK_v2.md
```

- [ ] **Step E1.3: Сопоставить slug → русское имя → OKLCH (по hue-порядку или по логическому)**

Стандартное mapping (вероятное):

| slug | русское имя (BRANDBOOK) | OKLCH-cell |
|---|---|---|
| `new` | Новая | (из BRANDBOOK) |
| `viewed` | Просмотрена | (из BRANDBOOK) |
| `worked` | В работе | (из BRANDBOOK) |
| `base` | База | (из BRANDBOOK) |
| `missed` | Не дозвонились | (из BRANDBOOK) |
| `negotiations` | Переговоры | (из BRANDBOOK) |
| `waiting_payment` | Ждёт оплату | (из BRANDBOOK) |
| `partnership` | Партнёрство | (из BRANDBOOK) |
| `paid` | Оплачено | (из BRANDBOOK) |
| `closed` | Закрыта | (из BRANDBOOK) |
| `test_drive` | Тест-драйв | (из BRANDBOOK) |
| `hot` | Горячая | (из BRANDBOOK) |
| `replacement` | Замена | (из BRANDBOOK) |
| `final_missed` | Финальный недозвон | (из BRANDBOOK) |

- [ ] **Step E1.4: Записать таблицу в BRANDBOOK_v2.md**

После §3.6 (14 OKLCH-статусов) добавить раздел:

```markdown
### 3.6.1. Mapping slug → имя → OKLCH (v2.1, audit P1-05)

Связывает 14 schema-slug'ов из `db/schema.sql:2170-2200` (INSERT lead_statuses) с 14 OKLCH-цветами из §3.6. Источник истины по slug'ам — schema.sql; по именам и цветам — этот brandbook. При расхождении приоритет — schema (см. CLAUDE.md §1).

| slug (schema) | Имя статуса (RU) | OKLCH-cell |
|---|---|---|
| `new` | Новая | (cell #1 §3.6) |
| `viewed` | Просмотрена | (cell #2) |
| ... | ... | ... |
| `final_missed` | Финальный недозвон | (cell #14) |

Если в новой версии schema добавляется/удаляется slug — обновить эту таблицу одновременно (CLAUDE.md §5 п.8: правки db/schema.sql ⇒ запись в db/CHANGELOG_schema.md ⇒ синхр. handoff).
```

Конкретные cell-номера — заполнить после Step E1.3 (по фактическому порядку в BRANDBOOK_v2 §3.6).

### Task E2: DEVELOPER_HANDOFF — axe-claim documentation + font-display

**Files:**

- Modify: `liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md` (строки 250 и 430+518)

- [ ] **Step E2.1: P1-04 — axe-claim documentation**

В `DEVELOPER_HANDOFF.md` найти строки с заявлением «0 violations» (~430, 518). Через Edit tool заменить (приблизительно):

Старое:

```text
Все прошли axe-core 4.10.2 = 0 violations на 1680/1440/768/375 (где применимо).
```

Новое:

```text
Все прошли axe-core 4.10.2 = 0 violations на 1680/1440/768/375 (где применимо). Дата прогона: <ДАТА из архива handoff'а или "требует подтверждения после фикса pa11y.config (см. audit P1-12, повторный прогон через `npm run a11y` на путях liderra_v8_handoff/concepts/v8_*.html)">. Отчёт axe в архиве — <путь к отчёту>, или "не сохранён, перепрогнать после P1-12 fix".
```

Если конкретный отчёт не найден в архиве — оставить пометку «требует подтверждения». Не удалять заявление (handoff = историческая фиксация).

- [ ] **Step E2.2: O-stack-09 — font-display strategy**

В `DEVELOPER_HANDOFF.md` найти блок §4 Типографика (строки ~250-260). После существующего описания шрифтов (Inter/JetBrains Mono) добавить раздел:

```markdown
### 4.X. Стратегия загрузки шрифтов (O-stack-09)

Используется `<link rel="stylesheet">` с Google Fonts API v1 + параметр `&display=swap`.

- **`&display=swap`:** браузер сразу рендерит fallback-шрифт (system-ui), переключается на загруженный Inter/JetBrains Mono когда тот доступен. Стратегия FOUT (Flash of Unstyled Text), но без невидимого текста — лучше для UX, чем `&display=block`.
- **WOFF2 формат:** Google Fonts отдаёт WOFF2 по умолчанию для современных браузеров (Chrome 36+, Firefox 39+, Safari 12+). Сжатие лучше WOFF на 20-30%.
- **Preconnect:** для ускорения первого byte рекомендуется добавить:

  ```html
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  ```

  Это сокращает TTFB на медленных сетях (~50-200 мс).

- **Совместимость:** для целевой аудитории Chrome 100+, Safari 15+, Firefox 95+ дополнительный fallback не нужен. Для расширенной аудитории — добавить @font-face с WOFF (legacy).

```

### Task E3: Verification Phase E

- [ ] **Step E3.1: Docs check**

```bash
npm run check:docs
```

Expected: markdownlint/cspell/lychee 0 errors на handoff'е (handoff включён в `npm run lint:md` через glob).

### Task E4: Commit Phase E

- [ ] **Step E4.1: Stage и commit**

```bash
git add liderra_v8_handoff/docs/BRANDBOOK_v2.md liderra_v8_handoff/docs/DEVELOPER_HANDOFF.md
git commit -m "$(cat <<'EOF'
docs(handoff): status-slug mapping table + axe-claim documentation + font-display strategy (audit P1-04/05 + O-stack-09)

Закрытие аудита 2026-05-09 (b6ae8dd) для handoff-блока:
- P1-05: BRANDBOOK_v2 §3.6.1 — таблица mapping 14 schema-slug ↔ имя статуса ↔ OKLCH-cell.
- P1-04: DEVELOPER_HANDOFF axe-claim связан с воспроизводимым evidence или пометкой
  "требует подтверждения после pa11y.config fix (P1-12)".
- O-stack-09: DEVELOPER_HANDOFF §4 — Font loading strategy (&display=swap + WOFF2 + preconnect).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Phase F. Registry

### Task F1: Открытые_вопросы — добавить P1-10, P1-11 link, P1-09

**Files:**

- Modify: `docs/Открытые_вопросы_v8_3.md`

- [ ] **Step F1.1: Прочитать структуру файла**

```bash
head -50 docs/Открытые_вопросы_v8_3.md
grep -nE "^### (Биз|CTO|Ю|Диз|DO|OPEN)-" docs/Открытые_вопросы_v8_3.md | head -20
```

- [ ] **Step F1.2: Найти максимальный CTO-номер для нового пункта**

```bash
grep -oE "CTO-[0-9]+" docs/Открытые_вопросы_v8_3.md | sort -t- -k2 -n | tail -3
```

Expected: например `CTO-17`, `CTO-18`. Новый = следующее число.

- [ ] **Step F1.3: Найти максимальный OPEN-номер**

```bash
grep -oE "OPEN-[0-9]+" docs/Открытые_вопросы_v8_3.md | sort -t- -k2 -n | tail -3
```

- [ ] **Step F1.4: Добавить новые записи**

Найти раздел открытых CTO-вопросов и добавить:

```markdown
### CTO-XX (новый, открыт 09.05.2026 по аудиту P1-10) — auth+tenant middleware на /api/deals на pre-prod миграции

**Контекст:** на MVP `/api/deals` использует `tenant_id` query-param для tenant-resolution (см. `app/routes/web.php:103-110`). На prod-миграции это **обязательно** перейти на `auth:sanctum + tenant` middleware с резолюцией через subdomain или header `X-Tenant-Id`.

**Trigger закрытия:** prod-миграция (после Б-1, регистрации ООО). До этого момента MVP-flow с query-param остаётся.

**Связанные находки:** [docs/audit_2026-05-09.md](audit_2026-05-09.md) P1-10.

**Состояние:** открыто.
```

В разделе **Б-1** (admin SSO) добавить (или дополнить существующий блок):

```markdown
> **Связанная находка аудита (P1-11):** `/api/admin/*` маршруты в `app/routes/web.php:74-99` без auth — тот же блокер. Закрывается одновременно с Б-1 (SSO Yandex 360).
```

Найти раздел OPEN-вопросов и добавить:

```markdown
### OPEN-XX (новый, открыт 09.05.2026 по аудиту P1-09) — Histoire ↔ Vite 8 миграционный долг

**Контекст:** Histoire 1.0-beta.1 заявляет peerDep `vite ^7`, у нас Vite 8. Установлен через `npm install --legacy-peer-deps` в `app/`. Smoke-test (`histoire build`) пройден; Vuetify auto-import регистрируется через `setupFile`.

**Trigger закрытия:** релиз Histoire с peerDep `vite ^8` (отслеживать [github.com/histoire-dev/histoire/releases](https://github.com/histoire-dev/histoire/releases)).

**Связанные находки:** [docs/audit_2026-05-09.md](audit_2026-05-09.md) P1-09.

**Состояние:** открыто, не блокирующее.
```

Заменить `XX` на актуальные номера из Step F1.2/F1.3.

### Task F2: Verification Phase F

- [ ] **Step F2.1: lychee + markdownlint**

```bash
npm run lint:md docs/Открытые_вопросы_v8_3.md
npm run links 2>&1 | grep -E "Errors|OK"
```

Expected: 0 errors.

- [ ] **Step F2.2: cspell**

```bash
npx cspell docs/Открытые_вопросы_v8_3.md
```

Expected: 0 issues.

### Task F3: Commit Phase F

- [ ] **Step F3.1: Stage и commit**

```bash
git add docs/Открытые_вопросы_v8_3.md
git commit -m "$(cat <<'EOF'
docs(registry): новые открытые вопросы по аудиту P1-10/11/09

Закрытие аудита 2026-05-09 (b6ae8dd) для registry-блока:
- P1-10: новый CTO-вопрос — auth+tenant middleware на /api/deals на pre-prod.
  Trigger: prod-миграция (после Б-1).
- P1-11: cross-link к Б-1 — /api/admin/* без auth = часть SSO-блока.
- P1-09: новый OPEN-вопрос — Histoire 1.0-beta.1 ↔ Vite 8 совместимость.
  Trigger: релиз Histoire с peerDep vite ^8.

Все 3 пункта аудита P1, не правки в коде. После закрытия Sprint 1
эти пункты остаются открытыми в реестре.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## DoD (соответствие spec §5)

После выполнения всех 6 фаз:

- [ ] Все 22 правки + 3 записи реестра применены, в 6 коммитах с осмысленными scope-сообщениями.
- [ ] `cd app && composer test` — Pest 416/416 PASS.
- [ ] `cd app && composer stan` — 0 errors above baseline.
- [ ] `cd app && npm run type-check` — 0 type errors.
- [ ] `cd app && npm run test:vue` — Vitest 393/393 PASS.
- [ ] `npm run check:docs` — 0 errors.
- [ ] Pre-commit hooks PASS на каждом из 6 коммитов.
- [ ] schema.sql v8.11 / 38 RLS / 97 индексов — подтверждено grep'ом.
- [ ] `git log --oneline -6` — видно 6 коммитов Sprint 1 после `b6ae8dd`.
- [ ] `docs/Открытые_вопросы_v8_3.md` содержит 3 новые/обновлённые записи (CTO-XX + Б-1 link + OPEN-XX).
- [ ] CLAUDE.md обновлён до v1.82, Pravila — до v1.7, Tooling — до v1.11.

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

- Реализация O-* за пределами 6 заявленных.
- Прогон pa11y на handoff концептах в браузере (manual после Phase C).
- Спринт 2 (modernization) и Спринт 3 (big refactors).

---

## Бюджет

| Фаза | Tasks | Wall-clock |
|---|---|---|
| A. DB | A1-A7 | 15-20 мин |
| B. Backend | B1-B7 | 30-40 мин |
| C. Configs | C1-C8 | 30-40 мин |
| D. Docs (narrative) | D1-D5 | 30-45 мин (через claude-md-management) |
| E. Docs (handoff) | E1-E4 | 30-45 мин |
| F. Registry | F1-F3 | 10-15 мин |
| **Итого** | **6 phases / 41 tasks** | **2.5-3.5 часа** |

---

## Risks

- **B6 (Pest fail):** регистрация tenant middleware на auth-routes может сломать тесты, не имеющие `SET LOCAL app.current_tenant_id`. Митигация — в Step B6.1 явная инструкция «STOP» при padении тестов; добавить fixture в test setup. Не пропускать падение.
- **C2 (lychee regex):** если regex для exclude захватит слишком много, потеряем покрытие реальных битых ссылок. Митигация — после Step C2.3 проверить число errors: должно быть близко к 0, но не строго 0 (могут остаться внешние URL).
- **D2 (claude-md-improver):** skill может сделать больше, чем заявлено в args (расширить scope). Митигация — Step D2.2 проверяет git status перед коммитом; если ≠ 3 файлов — STOP, разобраться.
- **E (handoff repo):** если `liderra_v8_handoff/` — не tracked в текущем git, Phase E пропускается с пометкой. Не блокирует Sprint 1.
- **F1 (CTO-/OPEN-номера):** Step F1.2/F1.3 динамически выбирают следующий номер. Если параллельно открывается другой вопрос — конфликт. Митигация — выполнять последовательно, без параллельных правок Открытые_вопросы.
- **D2/F1 кросс-влияние:** D2 правит CLAUDE.md/Pravila/Tooling, F1 правит Открытые_вопросы. Эти правки независимы, но обе требуют чистого dir state. Если есть pending изменения в registry — закоммитить отдельно.
