diff --git a/cspell-words.txt b/cspell-words.txt index 403b5ac7..4a8a3ced 100644 --- a/cspell-words.txt +++ b/cspell-words.txt @@ -21,6 +21,8 @@ uit фейковым pycache pyc +Категоризация +квирки # Возврат к Бренд + термины бэкап diff --git a/docs/superpowers/audits/2026-05-12-portal-full-audit-blocked.md b/docs/superpowers/audits/2026-05-12-portal-full-audit-blocked.md index 6306e35f..156d1cf7 100644 --- a/docs/superpowers/audits/2026-05-12-portal-full-audit-blocked.md +++ b/docs/superpowers/audits/2026-05-12-portal-full-audit-blocked.md @@ -67,6 +67,25 @@ **Что нужно от заказчика:** разрешить ли `pg_format -o db/schema.sql.formatted` + manual swap? Это вызовет **большой** noise diff в next PR review. **Если ответа нет → план Б:** **НЕ трогать** (стиль schema.sql ручной, defer на будущий refactor). +### Q.DEFER.002 — Live-URL a11y: 3 contrast violations (Phase 10 post-finalize) + +**Из:** Phase 10 Pa11y standalone scan на live `http://127.0.0.1:8000`. +**Severity при отказе:** P1 a11y compliance. +**Контекст:** + +- `app/resources/js/views/auth/ForgotPasswordView.vue` rate-limit alert — 2× contrast `4.18:1 < 4.5:1` (WCAG2AA G18). Vuetify `v-alert` info-variant background `~#fffcf5` recommended. +- `app/resources/js/views/errors/ErrorView.vue` support link — 1× contrast `2.77:1 < 4.5:1` для `support@liderra.app`. Pa11y recommend `#fcfffe` (false suggest — нужна диагностика computed-style). +- Auth-required views (8 main + 8 admin) **не проверены** — Pa11y standalone не auth'ится, требует cookie injection. + +**Что нужно от заказчика:** + +- (A) Согласовать дизайн-правку Vuetify theme (info-alert bg, text-primary contrast на ErrorView) с Платоном → fix через `resources/js/plugins/vuetify.ts` theme override. +- (B) Запустить mini-audit sub-task: diagnose ErrorView link computed-style через Playwright + fix targeted. +- (C) Расширить `pa11y.config.json` двумя списками + добавить в CI (ubuntu-latest) — long-term gate. +- (D) Defer до Б-1 / следующего design-pass. + +**Если ответа нет → план Б:** (B) для ErrorView (быстро, code-only) + (C) для CI coverage. Forgot-alert — отдельный design-tweak, defer. + ### Q.DEFER.001 — Memory `reference_archive.md` forward-stale description **Из:** Phase 6. diff --git a/docs/superpowers/audits/2026-05-12-portal-full-audit-findings.md b/docs/superpowers/audits/2026-05-12-portal-full-audit-findings.md index 4aa9e695..617dd117 100644 --- a/docs/superpowers/audits/2026-05-12-portal-full-audit-findings.md +++ b/docs/superpowers/audits/2026-05-12-portal-full-audit-findings.md @@ -459,6 +459,101 @@ activity_log, api_keys, auth_log, balance_transactions, comment_templates, deal_ --- +## Phase 10 — Live-URL Pa11y (extra, post-finalize) + +**Trigger:** заказчик «продолжай еще тут». Phase 5 audit запускал Pa11y только на `liderra_v8_handoff/concepts/v8_*.html` static HTML (config `pa11y.config.json` lines 22-35). Live портал не покрывался. Phase 10 closes this gap. + +**Method:** `npx pa11y --standard WCAG2AA --timeout 30000 --wait 1500 ` на 4 guest URLs (auth-required требуют session cookie — skipped в Phase 10, см. Q.DEFER.002 для следующей сессии). + +### Per-URL results + +| URL | Status | Errors | +|---|---|---| +| `/login` | ✅ clean | 0 | +| `/register` | ✅ clean | 0 | +| `/forgot` | ⚠️ 2 errors | 2× contrast `4.18:1 < 4.5:1` | +| `/no-such-path-404` (ErrorView) | ⚠️ 1 error | 1× contrast `2.77:1 < 4.5:1` | + +### Findings + +- **P1 [a11y WCAG2AA G18] `app/resources/js/views/auth/ForgotPasswordView.vue` rate-limit alert** — `
` + nested `` имеют contrast `4.18:1 < 4.5:1` для текста «Лимит — 5 попыток в 15 минут». **Pa11y recommends:** background `#fffcf5`. Корень — Vuetify `v-alert` info-variant tint. **[FIX-DEFER → blocked Q.DEFER.002]** — fix потребует либо theme-tweak в `resources/js/plugins/vuetify.ts` (default info-variant bg light shift), либо override class на specific alert, либо переоценки contrast pair (foreground text `#0F6E56` teal на info bg). Дизайнерское решение, не code-only. +- **P1 [a11y WCAG2AA G18] `app/resources/js/views/errors/ErrorView.vue` support link** — `` contrast `2.77:1 < 4.5:1`. **Pa11y recommends:** text color `#fcfffe` (но это white-on-white false suggest — Pa11y компьютит против wrong bg). Корень — Vuetify `text-primary` utility (= teal `#0F6E56`) на ErrorView body background. **3 гипотезы (systematic-debugging):** + - **H1**: ErrorView имеет gradient/illustration background где link рендерится поверх non-ivory color — contrast вычисляется правильно, но фактический pair неожиданный. + - **H2**: Pa11y видит `transparent` bg и computes против `body` или parent — false positive из-за CSS cascade. + - **H3**: `text-primary` фактически рендерит другой цвет в ErrorView context (CSS-var override?). + - **Falsify:** прочитать `ErrorView.vue` + computed-style на `support@liderra.app` link через Playwright `browser_evaluate`. Подтвердит H1 vs H2 vs H3. + - **[FIX-DEFER → blocked Q.DEFER.002]** — diagnose first, then fix. +- **P3 [audit-tooling] `pa11y.config.json` targets handoff concepts, не live** — `urls` (lines 22-35) указывают на `./liderra_v8_handoff/concepts/v8_*.html`. **[FIX-DEFER]** — расширить config двумя списками («handoff concepts» + «live SPA») с CI gate; вне scope этой сессии. + +### Phase 10 итог + +- P0: 0 +- P1: **2** (a11y violations на live + ErrorView; оба design-related → blocked.md Q.DEFER.002) +- P2: 0 +- P3: 1 (pa11y.config doesn't cover live) + +**Auth-required views (/dashboard, /projects, /deals, /kanban, /billing, /settings, /reminders, /admin/*) — NOT verified в Phase 10** (Pa11y standalone требует session cookie injection; defer to Q.DEFER.002 next session). + +--- + +## Phase 11 — TODO/FIXME sweep (extra, informational) + +**Method:** `Grep -E '\b(TODO|FIXME|XXX|HACK)\b'` over `app/**/*.{php,vue,ts}` (production + tests). +**Total:** 19 matches in 15 files. + +### Категоризация + +**MVP-defer ⏸ Б-1 (6, все cross-link Q.HARD.001):** + +- `app/resources/js/router/index.ts:184` — `// TODO: дополнительный role-guard на super_admin.` +- `app/app/Http/Controllers/Api/AdminSystemSettingsController.php:47` — `// TODO: $request->user()->id` (когда saas-admin auth готов) +- `app/app/Http/Controllers/Api/ImpersonationController.php:98` — `// TODO: $request->user()->id когда saas-admin auth готов` +- `app/app/Http/Controllers/Api/ImpersonationController.php:125` — `// TODO: отправить email на $tenant->contact_email с $plainCode.` +- `app/app/Http/Controllers/Api/ImpersonationController.php:212` — `// TODO: уведомление клиенту по email о завершении.` +- `app/resources/js/components/admin/ImpersonationDialog.vue:15` — `* chargeback_unrecovered_rub > 0 — TODO после saas-admin auth.` + +**Feature-defer (планы 5-7, не блокер):** + +- `app/app/Services/SupplierResolver.php:22` — fallback на `system_settings.default_supplier_id` +- `app/resources/js/views/settings/ProfileTab.vue:6` — `MVP: form-fields без save (TODO: PATCH /api/me)` +- `app/resources/js/views/auth/RecoveryCodesView.vue:14` — `TODO(phase2): получать из API после step1 настройки 2FA` +- `app/resources/js/views/auth/RecoveryCodesView.vue:47` — `TODO(phase2): редирект на /dashboard` +- `app/resources/js/views/projects/NewProjectDialog.vue:171` — `TODO: разобрать region_mask обратно в codes (Plan 6)` +- `app/resources/js/views/DealsView.vue:8` — `Не входит в этот коммит (отдельные TODO)` +- `app/resources/js/views/DashboardView.vue:9` — `Все числа сейчас mock'и — TODO: GET /api/dashboard/summary` +- `app/resources/js/components/kanban/KanbanCard.vue:8` — `Click → emit('open') — TODO: правая панель DealDetailDrawer` + +**Production-readiness (1):** + +- `app/app/Jobs/ProcessWebhookJob.php:375` — `// TODO(production): Sentry::captureException($e);` + +**Test infra (3, known квирки):** + +- `app/tests/Frontend/ProjectsView.spec.ts:68` — `TODO: VSelect dropdown в jsdom не открывает items-list через teleport` (квирк 54) +- `app/tests/Frontend/NewProjectDialog.spec.ts:69` — Vuetify-форма rendering в JSDOM (skip rationale) +- `app/tests/Frontend/NewProjectDialog.spec.ts:75` — same as 69 + +**False-positive (1):** + +- `app/tests/Feature/Auth/TwoFactorSetupTest.php:107` — `'totp_secret' => 'XXX'` (это **string literal**, не FIXME). Pattern match артефакт. + +### Findings + +- **P2 [tracking-hygiene] 6 TODO упирающихся в Б-1** — все ОК (documented MVP defer; cross-link Q.HARD.001). +- **P3 [tracking-hygiene] 8 feature-defer TODO** — все запланированы на Plan 6+ ИЛИ post-MVP. Не блокеры. **[FIX-DEFER]**. +- **P2 [production-readiness] `ProcessWebhookJob.php:375` Sentry::captureException** — должен быть включён до production launch. **[FIX-DEFER → blocked Q.PRODUCT.001 для prod-readiness checklist]**. +- **P3 [test-infra] 3 TODO про VSelect/Vuetify teleport в jsdom** — known квирк 54, documented. **[FIX-DEFER]**. +- **P3 [false-positive] XXX в TwoFactorSetupTest** — string literal, не FIXME. **Не находка.** + +### Phase 11 итог + +- P0: 0 +- P1: 0 +- P2: 2 (admin TODO group cross-link Б-1 + Sentry prod-readiness) +- P3: 12 (feature-defer + test-infra documented work) + +--- + ## Phase 8 — Fix log Будет заполнен по ходу выполнения.