# Portal-wide Audit & Proposals — Лидерра

**Дата:** 2026-05-15
**Автор:** Claude (через `superpowers:brainstorming` → собственная синтез)
**Заказчик:** Дмитрий (kpd9363@gmail.com)
**Статус:** approved для составления планов; реализация sprint-by-sprint по приоритету
**Связанные источники:** [docs/Открытые_вопросы_v8_3.md v1.83](../../Открытые_вопросы_v8_3.md), [docs/CRM_bp-gr_Инструкция_v8_5.md §6](../../CRM_bp-gr_Инструкция_v8_5.md), [CLAUDE.md v1.93](../../../CLAUDE.md)

---

## 1. Цель

Привести портал Лидерра к состоянию «всё, что обещает UI, работает» (полная функциональность для пользователя), включая опциональный модуль миграции исторических данных из crm.bp-gr.ru (§6 ТЗ), и убрать dev-артефакты перед production. Запрос заказчика 2026-05-15 night:

> «Проверь портал с точки зрения пользователя. Чтобы работали все кнопки, закладки, списки, справочники и т.д. Посмотри на содержание портала, убери или добавь того чего не хватает для полноценной работы портала и всех функций и задач, включая миграцию проектов и сделок. Сформулируй все предложения в табличной форме и дай на утверждение мне перед реализацией. Экономия 0%».

## 2. Метод аудита

| Шаг | Инструмент | Результат |
|---|---|---|
| Static-audit views/handlers/lookups | 4 параллельных `Explore`-агента (auth / app-user / admin / shared+stores+api) | 100+ raw findings с file:line |
| Карта маршрутов | `app/resources/js/router/index.ts` + `app/routes/web.php` | 26 SPA + 23 API endpoint'а |
| Sidebar coverage | `AppSidebar.vue` + `AdminLayout.vue` navItems | Расхождения с router'ом |
| ТЗ-coverage | ТЗ v8.5 §6 (CSV-импорт) + cross-ref schema.sql v8.20 | §6 спецификация vs текущая реализация (0%) |
| Реестр открытых вопросов | v1.83 (87 продуктовых / 71 ✅ / 11 ⏸) | Какие ⏸ внешние блокеры, какие можно закрыть в портале |
| Browser smoke | Playwright MCP — `/login` + console + network | Подтверждено: Vuetify работает, Lucide рендерит, `/api/auth/me` 401 для unauth (curl-500 был артефакт без Sanctum headers), DemoSeeder credentials расходятся с БД |

**Что НЕ проверялось этим аудитом:**

- Mobile-вид (всё спроектировано в desktop-first; mobile pass — отдельный спринт после готовности баз)
- Лендинг (⏸ Б-1, отдельный track)
- Real load-testing (Pre-prod track)
- Pen-test / SAST расширенный (Phase 3 трек Roadmap)

## 3. Sprint Schedule (по приоритету)

```
Sprint 1 (P0)  →  Sprint 2 (P1 wave 1)  →  Sprint 3 (P1 wave 2 + Backend gaps)
Sprint 4 (P1 миграция §6)  →  Sprint 5 (P2 UX-debt)  →  Sprint 6 (P3 polish + Cleanup)
```

### Sprint 1 — P0 BLOCKED-UX (5 эпиков, ~1.5-2 дня)

| ID | Title | Effort |
|---|---|---|
| C2 | FilterChip popovers DealsView (Проект/Менеджер) — реализовать списки из `/api/projects` + `/api/managers` | M |
| C4 | KanbanView DnD persist через `PATCH /api/deals/{id}` | S |
| C5 | BulkActionsBar (projects) — `window.alert` → `v-snackbar` | XS |
| G1 | AdminPricingTiersView — error handling submit/delete | XS |
| G2 | AdminSupplierPricesView — error handling save | XS |

### Sprint 2 — P1 wave 1: UI-функционал (Settings, Billing, Auth) (~5 эпиков, ~5-7 дней)

| ID | Title | Effort |
|---|---|---|
| A2 | RecoveryCodesView continue button — redirect | XS |
| A3 | RecoveryCodesView — подвязка на real API | S |
| A7 | `/legal/offer` + `/legal/privacy` routes + stub blade | S |
| D1+D2+D3+D4+D5+J5+J6 | Settings ProfileTab + ApiTab — все 6 кнопок + backend endpoints (`PATCH /me`, `POST /api-keys/regenerate`, `PUT /tenants/me/webhook-settings`, `POST /webhooks/test`, copy/regenerate logic) | L |
| E1 | BillingView Пополнить баланс — TopupDialog + `POST /api/billing/topup` (stub без реальной оплаты на MVP) | L |
| E3 | BillingView Overview tab — подвязка BalanceCard/TransactionsTable/InvoicesTable на real API | L |

### Sprint 3 — P1 wave 2: Admin + Dashboard + Reports + Reminders (~6 эпиков, ~4-6 дней)

| ID | Title | Effort |
|---|---|---|
| B1 | ❌ **won't-do** (2026-05-16) — ~~AppSidebar: добавить «Напоминания» в «Работа»~~. Конфликт с решением заказчика «sidebar cleanup» (commit `5c8ad27`): пункт убран намеренно. Реализован и откатан (revert `e0a3fb8`). | XS |
| B4 | AdminLayout sidebar: добавить /admin/pricing-tiers + /admin/supplier-prices | XS |
| B5 | AdminLayout impersonation-banner | M |
| C1+J3 | Dashboard real API + `GET /api/dashboard/summary` | M |
| C8+F3 | RemindersView openDeal + bell deep-link на `/deals?openId=` | S |
| D6+D7 | SettingsView placeholder-tabs — реализация или скрытие (decide per-tab) | M-L per tab |
| F1 | Reports Provider'ы — managers/sources/billing summary | M |
| F2 | Reports скачать-файл endpoint `/api/reports/jobs/{id}/file` + signed URL 24ч | S |
| G4 | AdminBillingView row-actions (suspend/refund/edit-tariff + backend endpoints) | L |
| G5 | AdminIncidentsView detail-view `/admin/incidents/:id` | M |
| G6 | AdminIncidentsView РКН-notify action + endpoint | S |
| J1 | CTO-18 — auth+tenant middleware на /api/deals (требует Б-1 для prod) | M |
| J2 | /api/admin/* — auth:saas-admin middleware (требует Б-1 + DO-4) | M |

### Sprint 4 — 🆕 Миграция исторических данных §6 (9 sub-tasks = 1 эпик, ~3-5 дней)

| ID | Title | Effort |
|---|---|---|
| H1 | Schema delta: `import_unknown_statuses` table + RLS | S |
| H2 | Schema enrichment `import_log` (+5 колонок) | XS |
| H3 | Backend сервисы: `CsvLeadsParser` + `StatusRuToSlugMapper` + `HistoricalImportService` | XL |
| H4 | Backend job + controller: `ImportLeadsJob` + `ImportController` + route + email | L |
| H5 | Frontend ImportView (dropzone + progress + результат-таблица) | L |
| H6 | Unknown statuses wizard — sub-dialog маппинга | M |
| H7 | Bulk-импорт проектов (18-колоночный CSV из crm.bp-gr.ru) | L |
| H8 | UI вход — tab в Settings + кнопка в ProjectsView page-head | XS (decision) |
| H9 | Мини-инструкция «Как перенести данные из crm.bp-gr.ru» | S |

**Schema delta:** v8.20 → v8.21 (+1 таблица + 5 колонок в import_log)

### Sprint 5 — P2 UX-debt (~19 эпиков, ~4-5 дней)

| ID | Title | Effort |
|---|---|---|
| A1 | Yandex SSO кнопка — `:disabled` + tooltip | XS |
| A4 | ResetPassword confirmation `:error-messages` | XS |
| A5 | ForgotPassword fallback message logic fix | XS |
| A6 | TwoFactor реальный countdown | S |
| A8 | DemoSeeder re-seed скрипт + dev README | XS |
| B2 | Sidebar count «Сделки» из store + `/api/deals?count_only=1` (J4) | S |
| B3 | ⌘K — minimal command palette ИЛИ удалить плашку | M (decide) |
| C3 | DealsView Export button consolidation | XS |
| C6 | NewDealDialog degradation alert при mock-fallback | XS |
| C7 | DealDetailDrawer degradation alert | XS |
| C9 | ProjectsView Plan 6 (regions subject-level) — review + merge после GitHub 403 unblock | S |
| E2 | BalanceCard «Автопополнение» + «Сменить тариф» — disable+tooltip ИЛИ реализовать | XS / L |
| E4 | BillingView pending alert — real data | S |
| G3 | AdminPricingTiers + SupplierPrices — переписать на TypeScript | M |
| G7 | AdminPricingTiers effective_from date-picker | S |
| G10 | AdminBilling `confirm()` → `v-dialog` | XS |
| I1 | DevIndexBadge + DevIndexOverlay cleanup (если решено убрать перед production) | S |
| I3 | mocks → `tests/__mocks__/` или DEV-only feature-flag | S |
| I4 | ImpersonationDialog devPlainCode — gate за DEV | XS |

### Sprint 6 — P3 polish + Cleanup tail (~8 эпиков, ~2-3 дня)

| ID | Title | Effort |
|---|---|---|
| A9 | A11y aria-labels на eye-icons | XS |
| B6 | AdminLayout DEV-only auth-gap banner | XS |
| F4 | usePolling → constants/polling.ts | XS |
| F5 | `new_device_login` через session-fingerprint (требует инфра) | XL |
| G8 | ImpersonationDialog two-person approval (требует Б-1 SSO) | M |
| G9 | AdminSystem aria-label на edit-кнопки | XS |
| I2 | dev-indices.json gitignore-решение | XS |
| I5 | ProjectsView clearable workaround removal (CTO-19 closure tail) | XS |

## 4. Таблица предложений — полный список 70 эпиков

> Полная таблица с обоснованиями — см. оригинальный sync-message от 2026-05-15 в conversation. Ниже компактная версия с ссылками.

### A. Аутентификация и onboarding (9 эпиков)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| A1 | `LoginView.vue:107` — Yandex 360 SSO | dead stub (по плану — ⏸ Б-1) | `:disabled` + tooltip «после Б-1» | 🟡 P2 | XS |
| A2 | `RecoveryCodesView.vue:73` | TODO no-op | redirect на /login или /dashboard | 🟠 P1 | XS |
| A3 | `RecoveryCodesView.vue:15-24` | Статический mock | API integration | 🟠 P1 | S |
| A4 | `ResetPasswordView.vue:110-118` | Нет `:error-messages` | Add error display | 🟡 P2 | XS |
| A5 | `ForgotPasswordView.vue:36-38` | Fallback недостижим | Безусловный fallback | 🟡 P2 | XS |
| A6 | `TwoFactorView.vue:129` | Hardcoded «02:34» | Реальный countdown | 🟡 P2 | S |
| A7 | `AuthLayout.vue:47-48` | `/legal/*` → 404 | Stub-routes + blade | 🟠 P1 | S |
| A8 | DemoSeeder dev | 422 при логине | Re-seed скрипт + README | 🟡 P2 | XS |
| A9 | Eye-icons toggle | Нет aria-label | Добавить | 🟢 P3 | XS |

### B. Sidebar / Layout / Navigation (6 эпиков)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| B1 | AppSidebar — Напоминания | Отсутствует в nav (намеренно, `5c8ad27`) | ❌ **won't-do** (2026-05-16): re-add конфликтует с решением заказчика «sidebar cleanup»; пункт остаётся убранным | 🟠 P1 | XS |
| B2 | AppSidebar Сделки count=247 | Hardcoded | computed из store | 🟡 P2 | S |
| B3 | ⌘K stub в sidebar+topbar | Без handler | Command palette или удалить | 🟡 P2 | M |
| B4 | AdminLayout navItems | Нет /admin/pricing-tiers и /admin/supplier-prices | Добавить | 🟠 P1 | XS |
| B5 | Impersonation banner | Нет глобального индикатора | Полоса в AdminLayout | 🟠 P1 | M |
| B6 | AdminLayout auth-gap | Не виден в UI | DEV-only banner | 🟢 P3 | XS |

### C. App-views Dashboard/Deals/Kanban/Projects (9 эпиков)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| C1 | Dashboard KPI mock | TODO | `GET /api/dashboard/summary` | 🟠 P1 | M |
| C2 | DealsView FilterChip popovers | console.log only | Popover'ы со списками | 🔥 P0 | M |
| C3 | DealsView page-head Export | Заглушка | Подвязать или удалить | 🟡 P2 | XS |
| C4 | KanbanView DnD persist | TODO | `PATCH /api/deals/{id}` | 🔥 P0 | S |
| C5 | BulkActionsBar window.alert | Блокирует UI | `v-snackbar` | 🔥 P0 | XS |
| C6 | NewDealDialog mock fallback | Silent | Degradation alert | 🟡 P2 | XS |
| C7 | DealDetailDrawer MOCK_EVENTS | Silent | Degradation alert | 🟡 P2 | XS |
| C8 | RemindersView openDeal | Пусто | `/deals?openId=` deep-link | 🟠 P1 | S |
| C9 | ProjectsView Plan 6 regions | Локально в worktree | Review + merge после GitHub unblock | 🟠 P1 | S |

### D. Settings (7 эпиков)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| D1 | ProfileTab Save | Без handler | `PATCH /api/auth/me` (J6) | 🟠 P1 | M |
| D2 | ApiTab Copy | Без handler | clipboard.writeText + toast | 🟠 P1 | XS |
| D3 | ApiTab Regenerate | Без handler | `POST /api/api-keys/regenerate` (J5) | 🟠 P1 | M |
| D4 | ApiTab Save Webhook | Без handler | `PUT /api/tenants/me/webhook-settings` (J5) | 🟠 P1 | S |
| D5 | ApiTab Test Webhook | Без handler | `POST /api/webhooks/test` (J5) | 🟠 P1 | S |
| D6 | PlaceholderTab × 4 | «В разработке» | Реализовать или скрыть | 🟠 P1 | L per tab |
| D7 | SettingsView left-rail | 8 tab'ов, 4 заглушки | Hide-if-not-implemented + добавить «Импорт» (H8) | 🟡 P2 | XS |

### E. Billing (4 эпика)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| E1 | BillingView Пополнить | Без handler | TopupDialog + `POST /api/billing/topup` stub | 🟠 P1 | L |
| E2 | BalanceCard 3 кнопки | Без handlers | Disable+tooltip или реализация | 🟡 P2 | XS/L |
| E3 | BillingView Overview API | MOCK | Real API | 🟠 P1 | L |
| E4 | BillingView pending alert | Hardcoded | Real data | 🟡 P2 | S |

### F. Reports / Reminders / Notifications (5 эпиков)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| F1 | Reports Provider'ы | 1 из 4 | Реализовать 3 оставшихся | 🟠 P1 | M |
| F2 | Reports Скачать | Endpoint нет | `/api/reports/jobs/{id}/file` + signed URL | 🟠 P1 | S |
| F3 | bell deep-link | `/deals` без id | `/deals?openId=` (C8) | 🟡 P2 | XS |
| F4 | usePolling 30s hardcoded | Разбросано | `constants/polling.ts` | 🟢 P3 | XS |
| F5 | new_device_login | Заглушка | Session-fingerprint (Post-MVP) | 🟢 P3 | XL |

### G. Admin SaaS (10 эпиков)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| G1 | PricingTiersView submit/delete | Silent failures | try/catch + alert + toast | 🔥 P0 | XS |
| G2 | SupplierPricesView save | Silent failures | try/catch + alert + toast | 🔥 P0 | XS |
| G3 | PricingTiers + SupplierPrices — plain JS | Нет типизации | TS rewrite + `api/admin.ts` | 🟡 P2 | M |
| G4 | AdminBillingView row-actions | Нет actions | Dropdown 3 actions + backend | 🟠 P1 | L |
| G5 | AdminIncidentsView drill-down | Нет detail-view | `/admin/incidents/:id` | 🟠 P1 | M |
| G6 | AdminIncidentsView РКН-notify | Badge без action | Button + endpoint | 🟠 P1 | S |
| G7 | PricingTiersView effective_from | Hardcoded next month | Date-picker | 🟡 P2 | S |
| G8 | ImpersonationDialog two-person | TODO (CTO-15) | Реализация после Б-1 SSO | 🟢 P3 | M |
| G9 | AdminSystem aria-label | Нет на edit-кнопках | Добавить | 🟢 P3 | XS |
| G10 | AdminBilling `confirm()` | Браузерный | `v-dialog` confirm | 🟡 P2 | XS |

### H. Миграция §6 — 🆕 НОВЫЙ ЭПИК (9 sub-tasks)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| H1 | Schema delta | `import_unknown_statuses` отсутствует | Migration + RLS | 🆕 P1 | S |
| H2 | import_log enrichment | Минимальный | +5 колонок (entity_type, source_system, mapping_config, unknown_statuses_count, dry_run) | 🆕 P1 | XS |
| H3 | Backend services | Нет | `CsvLeadsParser` + `StatusRuToSlugMapper` + `HistoricalImportService` (advisory-lock per §6.5, balance НЕ списывается, transaction type `historical_import`) | 🆕 P1 | XL |
| H4 | Backend job + controller | Нет | `ImportLeadsJob` + `ImportController` + route `/api/imports` + email-уведомление | 🆕 P1 | L |
| H5 | Frontend ImportView | Нет | Dropzone + progress + результат-таблица | 🆕 P1 | L |
| H6 | Unknown statuses wizard | Нет | Sub-dialog маппинга | 🆕 P1 | M |
| H7 | Bulk-импорт проектов | Нет | Аналогичный flow для CSV проектов | 🆕 P1 | L |
| H8 | UI вход — где | Нет | Tab в Settings + кнопка в ProjectsView | 🆕 P1 | XS |
| H9 | Мини-инструкция | Нет | Markdown «Как перенести данные» | 🆕 P2 | S |

**ТЗ референс:** §6.1 (когда нужен) / §6.2 (формат файла) / §6.3 (маппинг полей) / §6.4 (русские названия → slug) / §6.5 (идемпотентность через `webhook_dedup_keys`) / §6.6 (workflow в UI) / §6.7 (таблица `import_log`).

### I. Dev-артефакты для production hygiene (5 эпиков)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| I1 | DevIndexBadge + DevIndexOverlay | Temporary feature | Удалить перед production | 🧹 P2 | S |
| I2 | dev-indices.json | Modified in git status | .gitignore decision | 🧹 P3 | XS |
| I3 | composables/mock*.ts | Production fallbacks | `tests/__mocks__/` или DEV-flag | 🧹 P2 | S |
| I4 | devPlainCode banner | Visible в UI | DEV-only gate | 🧹 P2 | XS |
| I5 | ProjectsView clearable `::after` | Workaround | Удалить (CTO-19 closure tail) | 🧹 P3 | XS |

### J. Backend / Infra gaps (6 эпиков)

| ID | Объект | Сейчас | Предложение | Приоритет | Effort |
|---|---|---|---|---|---|
| J1 | CTO-18 middleware на /api/deals | Реестр-open | auth:sanctum+tenant перед prod | 🟠 P1 | M |
| J2 | /api/admin/* middleware | Per Б-1 ⏸ | auth:saas-admin после Б-1+DO-4 | 🟠 P1 | M |
| J3 | GET /api/dashboard/summary | Нет | Создать для C1 | 🟠 P1 | M |
| J4 | GET /api/deals?count_only=1 | Нет | Создать для B2 | 🟡 P2 | XS |
| J5 | API-keys/Webhooks endpoints | Нет | 3 новых endpoint'а для D2-D5 | 🟠 P1 | M |
| J6 | PATCH /api/auth/me | Только notification-preferences | Расширить для full profile (D1) | 🟠 P1 | S |

### K. Out-of-scope (намеренно НЕ меняем)

| ID | Объект | Причина |
|---|---|---|
| K1 | Lucide иконки | CTO-19 ✅ closed |
| K2 | region_mask legacy | Plan 6.5 cleanup |
| K3 | Реестровые ⏸ внешние блокеры | Б-1 / юр.редактура / DO-2/DO-4 / Биз-25..31 |
| K4 | MagicLink 24ч | Биз-13 closed |
| K5 | Histoire stories | Dev-tool |

## 5. Сводка

| Категория | Кол-во | 🔥 P0 | 🟠 P1 | 🟡 P2 | 🟢 P3 | 🆕 NEW | 🧹 CLEAN |
|---|---|---|---|---|---|---|---|
| A. Auth | 9 | 0 | 3 | 5 | 1 | 0 | 0 |
| B. Sidebar/Layout | 6 | 0 | 3 | 2 | 1 | 0 | 0 |
| C. App-views | 9 | 3 | 4 | 2 | 0 | 0 | 0 |
| D. Settings | 7 | 0 | 6 | 1 | 0 | 0 | 0 |
| E. Billing | 4 | 0 | 2 | 2 | 0 | 0 | 0 |
| F. Reports/etc | 5 | 0 | 2 | 1 | 2 | 0 | 0 |
| G. Admin | 10 | 2 | 4 | 3 | 1 | 0 | 0 |
| H. Миграция §6 | 9 | 0 | 0 | 1 | 0 | 8 | 0 |
| I. Cleanup | 5 | 0 | 0 | 3 | 2 | 0 | 5 |
| J. Backend gaps | 6 | 0 | 5 | 1 | 0 | 0 | 0 |
| **Итого** | **70** | **5** | **29** | **21** | **7** | **8** | **5** |

**Realistic эпиков после группировки:** ~30-35.
**Грубая оценка solo-разработчика:** 6-9 недель (Sprint 1 ~2 дня, Sprint 2 ~7 дней, Sprint 3 ~6 дней, Sprint 4 ~5 дней, Sprint 5 ~4 дня, Sprint 6 ~3 дня, + buffer на code-review/testing/regression).

## 6. Risks & dependencies

| Risk | Митigation |
|---|---|
| GitHub push BLOCKED 403 «verify email» (Plan 6 + сегодняшние коммиты) | До закрытия — работа локально + worktree. Б-1 (ООО) не требуется для этого, нужен GitHub-account fix |
| Б-1 (реквизиты ООО) блокирует J1 (полный production переход), J2 (saas-admin SSO), E1 (реальная ЮKassa), Диз-3/DO-2/DO-4 | Все эпики на MVP идут со stub-аутентификацией / placeholder-данными. Готовим код, но production-deploy ждёт Б-1 |
| Биз-28 (CSV-схема поставщика) — BLOCKED credentials | Не блокирует H1-H9 (миграция работает с CSV ТЗ-формата §6.2 от пользователя, не от поставщика) |
| Schema delta v8.20→v8.21 в Sprint 4 | Migrate:fresh на dev в начале спринта; PHPStan baseline regen; обновить CLAUDE.md §0/§2 после merge |
| Sprint 5 (P2 UX) — большой объём | Можно дробить на 2 sub-sprint'а если устаёт фокус |
| Mock-data fallback removal (I3) | Verify нет production-paths использующих mocks перед удалением — может потребовать feature-flag вместо удаления |

## 7. Acceptance criteria для каждого Sprint'а

- **Sprint 1 (P0):** Все 5 P0-эпиков закрыты + regression Pest --parallel + Vitest зелёные + browser smoke по каждому fix'у.
- **Sprint 2 (P1 wave 1):** Settings (Profile/Api) + Billing (Topup) полностью функциональны от UI до DB. Auth legal-pages не выдают 404.
- **Sprint 3 (P1 wave 2):** Dashboard живой. Admin row-actions + impersonation banner + incidents drill-down + reports backend complete. Sidebar (B1, B4) исправлен.
- **Sprint 4 (миграция §6):** Тенант может загрузить CSV из crm.bp-gr.ru, увидеть прогресс, замапить unknown statuses, получить email-результат. Аналогично для проектов.
- **Sprint 5 (P2):** ⌘K либо работает, либо удалён. Все toast'ы консистентны. Demo-seeder работает на чистом dev'е. Dev-артефакты удалены/гейтнуты.
- **Sprint 6 (P3):** A11y aria-labels везде. Polling вынесен. Cleanup tail.

## 8. Approval log

- 2026-05-15 night — Дмитрий: «Полный portal-wide + браузерный smoke (Recommended)» (scope)
- 2026-05-15 night — Дмитрий: «Да, всё в работу — спринты по приоритету P0→P1→P2→P3+Новые (Recommended)» (composition + scheduling)
- 2026-05-16 — Дмитрий: B1 «Напоминания в сайдбар» → ❌ **won't-do** — re-add конфликтует с прежним решением заказчика «sidebar cleanup» (commit `5c8ad27`, тест `AppLayout.spec.ts` фиксирует «убраны по требованию заказчика»). Sprint 3A реализовал B4 + B5; B1 реализован и откатан (revert `e0a3fb8`).

## 9. Next steps

После approval этого spec'а:

1. `git add` + `git commit` этого spec-файла.
2. Запустить `superpowers:writing-plans` для **Sprint 1** (5 P0-эпиков). Когда Sprint 1 закрыт — Sprint 2 и т.д.
3. Между спринтами — verification-before-completion regression sweep (Pest --parallel + Vitest + vue-tsc + lefthook).
4. Обновление CLAUDE.md / реестра / memory после каждого закрытого спринта (через `claude-md-management:revise-claude-md`).
