diff --git a/docs/superpowers/specs/2026-05-15-portal-audit-design.md b/docs/superpowers/specs/2026-05-15-portal-audit-design.md new file mode 100644 index 00000000..5e4002d2 --- /dev/null +++ b/docs/superpowers/specs/2026-05-15-portal-audit-design.md @@ -0,0 +1,321 @@ +# 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 | AppSidebar: добавить «Напоминания» в группу «Работа» | 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 | Добавить в «Работа» | 🟠 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) + +## 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`).