docs(spec-sprint1): дизайн Спринта 1 «Hygiene» исправления аудита 2026-05-09
Spec через superpowers:brainstorming. Scope: 22 правки + 3 записи в реестр: - 3 P0 (RLS impersonation_tokens, SetTenantContext middleware, format:sql:check Windows) - 10 P1 (P1-10/P1-11 → реестр) - 3 P2 (test password, орфо, F-K расшифровка) - 6 low-risk O-* (2 FK indices, password rules trait, ESLint anti-vuetify, npm CI, font-display docs) 6 фаз / 6 коммитов: A.DB → B.Backend → C.Configs → D.Docs(narrative) → E.Docs(handoff) → F.Registry. Зависимости: B/D зависят от A (CHANGELOG schema метрики). Бюджет: 2.5-3.5 часа агентов в субагентном режиме. CLAUDE.md/Pravila/Tooling правки в Фазе D — обязательно через claude-md-management:claude-md-improver (CLAUDE.md §5 п.10). Не входит: Спринт 2 (modernization) и Спринт 3 (big refactors) — отдельные spec'и. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -853,6 +853,7 @@ SRP
|
||||
коммитится
|
||||
OKLCH
|
||||
ребрендинга
|
||||
gstatic
|
||||
|
||||
# v1.81 — Plugin Stack Rules v1.3 (Frontend Design + Superpowers paired stack)
|
||||
инвокация
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
# 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 История версий](docs/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.
|
||||
Reference in New Issue
Block a user