# Скилл `/regression` — канонический регрессионный свод — Design Spec

**Дата:** 2026-05-16
**Статус:** дизайн одобрен заказчиком (через `superpowers:brainstorming`, Q1–Q4 + полное согласование дизайна)
**Экономия:** 0% (максимальное качество, без скипов)
**Связанные артефакты:** `.claude/skills/regression/SKILL.md` (создаётся), `.claude/skills/regression/run.mjs` (создаётся), `.claude/skills/regression/run.test.mjs` (создаётся), `.claude/skills/q-item-add/SKILL.md` (образец), `.claude/skills/rls-check/SKILL.md` (образец), `tools/ruflo-queen-hook.test.mjs` (образец теста), `package.json`, `app/package.json`, `app/composer.json`, `lefthook.yml`
**Источник:** рекомендация №1 из `/claude-code-setup:claude-automation-recommender` (16.05.2026)
**Следующий шаг:** `superpowers:writing-plans` → план реализации.
**Правка 1 (2026-05-16, при `writing-plans`):** §2/§3/§4/§10 — добавлен co-located `run.test.mjs` (`node:test`). Прежняя формулировка §10 (об отсутствии отдельных unit-тестов для `run.mjs`) опиралась на ложную предпосылку («`tools/*.mjs` тестов не имеют»); фактически все 3 `tools/*.mjs` имеют co-located `.test.mjs`. Скорректировано под economy-0% TDD-мандат + конвенцию проекта.

---

## 1. Контекст и проблема

Перед каждым закрытием задачи/спринта проект «Лидерра» прогоняет один и тот же регрессионный свод и записывает его результат в `CLAUDE.md` канонической строкой вида `Pest 742/739/3sk/0 / Vitest 92f/774/3sk/0 / Vite build 2.03s / gitleaks 0/442 / lychee 325/0`. Эта строка повторяется в `CLAUDE.md` и memory десятки раз (v1.86–v2.1, audit-записи, sprint-closure-записи).

Сами команды свода **разбросаны** по четырём файлам и двум рабочим директориям:

- корневой `package.json` — `lint:md`, `spell`, `links`, `a11y`, `sast`;
- `app/package.json` — `lint:vue`, `format:check`, `type-check`, `test:vue`, `build`;
- `app/composer.json` — `test`, `pint:test`, `test:parallel`, `stan`;
- `lefthook.yml` — gitleaks (staged + full-history), lychee.

Следствия отсутствия единого входа:

1. **Ручная оркестрация** — Claude/человек запускает ~12 команд вручную, по одной, помня про смену `cwd` (`app/` vs корень).
2. **Недетерминированность отчёта** — итоговая каноническая строка собирается «на глаз»; формат и полнота зависят от исполнителя; возможен пропуск проверки.
3. **Нет жёсткого вердикта** — «зелёно ли» определяется чтением вывода, а не машинно.
4. **Дублирование в `verification-before-completion`** — дисциплина «проверь перед claim `готово`» каждый раз заново перечисляет тот же свод.

Скилл `/regression` устраняет всё перечисленное: один вызов запускает свод, печатает каноническую строку детерминированно и даёт машинный вердикт GREEN/RED.

## 2. Цель и границы

**Цель.** Упаковать регрессионный свод в один скилл Claude Code с двумя уровнями объёма, детерминированным bundled-оркестратором и exit-code-вердиктом.

**В scope v1:**

- два уровня — `quick` (линт/формат/тип) и `full` (весь свод с прогоном тестов);
- bundled-скрипт-оркестратор на Node ESM;
- каноническая строка + per-check таблица + вердикт GREEN/RED;
- правила инвокации (Both с self-restraint);
- caveats по проектным квиркам (Pest `--parallel` flake, ruflo daemon);
- co-located `run.test.mjs` (`node:test`) — unit-тесты pure-функций `run.mjs` + subprocess-тест `main`.

**Не-цели (сознательно вне scope v1):**

- сравнение с хранимым baseline и авто-флаг регрессий по числам (отклонено в Q4 — дрейф baseline, ручной ре-синк);
- автоматическая правка `CLAUDE.md` канонической строкой (нарушило бы `CLAUDE.md` §5 п.10 — канал `claude-md-management`);
- параллельный запуск проверок (возможная поздняя оптимизация, YAGNI для v1);
- Pa11y / Semgrep SAST / knip / infection (CI-tier или слишком медленные — см. §5);
- интеграция в CI (скрипт пригоден к вызову из CI, но workflow-файлы — вне scope);
- формализация скилла в `Tooling_v8_3.md` Прил. Н (возможный follow-up — см. §13).

## 3. Архитектурные решения

Четыре развилки согласованы заказчиком через `AskUserQuestion` (Q1–Q4); три детали приняты Claude единолично и одобрены при согласовании дизайна.

| # | Развилка | Решение | Обоснование |
|---|---|---|---|
| Q1 | Объём | Два уровня: `quick` + `full` | `quick` — быстрый фидбэк по ходу работы; `full` — полный свод перед закрытием. Соответствует реальному workflow. |
| Q2 | Реализация | Bundled-скрипт-оркестратор | Детерминизм канонической строки и полнота прогона — ядро ценности скилла; instruction-only их не гарантирует. |
| Q3 | Инвокация | Both с self-restraint | Синергия с `verification-before-completion` (Claude авто-`quick`) + контроль дорогого `full`-прогона. |
| Q4 | Вердикт | Run + report + exit-code, без baseline | Exit-коды уже отвечают на «зелёно?»; хранимый baseline дрейфует и требует ручного ре-синка. YAGNI. |
| U1 | Дефолт без аргумента | `/regression` ≡ `/regression full` | Headline use-case — свод перед закрытием. |
| U2 | Состав `quick` | `quick` НЕ включает Larastan | Чистая граница «линт/формат/тип» vs «статанализ/прогон»; Larastan уже в `lefthook` pre-commit. |
| U3 | Язык скрипта | Node `.mjs` | House-style проекта: `tools/ruflo-*.mjs`, `app/scripts/dev-indices-lookup.mjs`, `node -e` в хуках `.claude/settings.json`; кросс-платформенно для Linux/Mac CI. |
| P1 | Тестирование `run.mjs` | Co-located `run.test.mjs` (`node:test`): unit-тесты pure-функций + subprocess-тест `main` | Правка 1 — economy-0% TDD-мандат + конвенция проекта (все `tools/*.mjs` имеют `.test.mjs`, паттерн `tools/ruflo-queen-hook.test.mjs`). |

## 4. Компоненты

Скилл — самодостаточный каталог `.claude/skills/regression/` рядом с существующими `q-item-add` и `rls-check`.

| Файл | Назначение |
|---|---|
| `.claude/skills/regression/SKILL.md` | frontmatter (`name`, `description`, инвокация) + «Когда использовать» + «Workflow» + «Правила инвокации (self-restraint)» + «Caveats» + «Не использовать когда». Структура повторяет `q-item-add`/`rls-check`. |
| `.claude/skills/regression/run.mjs` | Оркестратор. Аргумент `quick` \| `full` (дефолт `full`). Чистый Node ESM — только `node:child_process`, `node:process`, `node:path`; без npm-зависимостей. Экспортирует pure-функции (`parse`-функции проверок, логика вердикта, сборка канонической строки, платформенная развилка) для unit-тестирования. |
| `.claude/skills/regression/run.test.mjs` | Co-located тесты (`node:test` + `node:assert/strict`), паттерн `tools/ruflo-queen-hook.test.mjs`: unit-тесты экспортированных pure-функций + subprocess-тесты `main` через `execFileSync`. |

`SKILL.md` frontmatter (Both с self-restraint — `disable-model-invocation` НЕ ставится, в отличие от `q-item-add`/`rls-check`):

```yaml
---
name: regression
description: |
  Run the project regression sweep and report a canonical status line + GREEN/RED verdict.
  Two tiers: `quick` (lint/format/type-check — seconds) and `full` (everything incl.
  Pest --parallel, Larastan, Vitest, Vite build, lychee, gitleaks — minutes).
  Claude auto-runs only `quick` (e.g. during verification-before-completion);
  `full` runs only on explicit `/regression full` or with user confirmation.
---
```

`description` — на английском (конвенция frontmatter скиллов Claude Code; тело `SKILL.md` — на русском по `Pravila` §3.6). Self-restraint выражен в `description` и подробно — в теле «Правила инвокации».

## 5. Матрица проверок

`quick` — строгое подмножество `full`. `full` опирается на существующие npm/composer-скрипты; прямой вызов бинаря — только у gitleaks и lychee (их npm-скрипт `links` хардкодит `bin\lychee.exe`, что ломается на POSIX, поэтому оркестратор вызывает оба бинаря сам с платформенной развилкой).

| # | Проверка | quick | full | cwd | команда |
|---|---|:--:|:--:|---|---|
| 1 | Pint (PHP style) | ✅ | ✅ | `app/` | `composer pint:test` |
| 2 | ESLint | ✅ | ✅ | `app/` | `npm run lint:vue` |
| 3 | Prettier `--check` | ✅ | ✅ | `app/` | `npm run format:check` |
| 4 | vue-tsc (type-check) | ✅ | ✅ | `app/` | `npm run type-check` |
| 5 | markdownlint | ✅ | ✅ | корень | `npm run lint:md` |
| 6 | cspell | ✅ | ✅ | корень | `npm run spell` |
| 7 | Larastan | — | ✅ | `app/` | `composer stan` |
| 8 | Pest `--parallel` | — | ✅ | `app/` | `composer test:parallel` |
| 9 | Vitest | — | ✅ | `app/` | `npm run test:vue` |
| 10 | Vite build | — | ✅ | `app/` | `npm run build` |
| 11 | lychee (links) | — | ✅ | корень | win32: `bin\lychee.exe --config .lychee.toml "docs/**/*.md" "db/**/*.md" "*.md"`; POSIX: `lychee` + те же аргументы |
| 12 | gitleaks | — | ✅ | корень | win32: `bin\gitleaks.exe detect --source . --no-banner --config .gitleaks.toml --redact`; POSIX: `gitleaks` + те же аргументы |

Итого: `quick` — 6 проверок, `full` — 12.

**Сознательно исключены** (заказчик может попросить добавить):

- **Pa11y** — CI-only, требует Chromium; `CLAUDE.md` §5 п.3 и `lefthook.yml` явно держат его только в CI.
- **Semgrep SAST** (`npm run sast`) — CI-tier; не входит в каноническую строку.
- **knip** (dead-code) — не входит в каноническую строку; разовый P3-инструмент.
- **infection** (мутационное тестирование) — слишком медленно для регрессионного свода.

## 6. Модель исполнения

- **Запускать ВСЕ проверки уровня; не останавливаться на первом провале.** Оркестратор прогоняет каждую проверку до конца и агрегирует результаты — нужна полная картина, а не первый failure.
- **Последовательно** в v1. Pest `--parallel` уже сатурирует ядра; параллельный Vitest рядом дал бы CPU-трешинг и contention тестовых БД. Порядок: сначала быстрые (1–6), затем тяжёлые (7–12) — ранний фидбэк по дешёвым проверкам.
- Для каждой проверки оркестратор фиксирует: `id`, exit-код, wall-time, и распарсенный count-токен для канонической строки.
- Платформенная развилка: `process.platform === 'win32'` → `bin\*.exe` для gitleaks/lychee, иначе бинарь на `PATH` (`CLAUDE.md` §4 — на Linux/Mac CI gitleaks/lychee ставятся через brew/apt).
- Смена `cwd` — оркестратор резолвит корень репозитория от расположения `run.mjs` (`../../..` от `.claude/skills/regression/`) и запускает app-проверки в `<root>/app`.

**Структура данных проверки** (концептуально, детали — в плане реализации):

```js
{ id: 'pest', label: 'Pest', tiers: ['full'], cwd: 'app',
  cmd: ['composer', 'test:parallel'],
  parse: (stdout, stderr, code) => 'Pest 742/739/3sk/0' }
```

Каждая проверка несёт собственный `parse` — мелкую функцию, извлекающую count-токен из вывода. Токены проверок 1–6 (линт/формат/тип) сводятся к exit-коду (`Pint 0`, `vue-tsc 0`); проверки 7–12 несут содержательный count (`Larastan 2`, `Pest 742/739/3sk/0`, `Vite build 2.0s`, `lychee 325/0`, `gitleaks 0/442` — см. §7).

## 7. Формат вывода

Оркестратор печатает в stdout три блока: заголовок + per-check таблица, каноническая строка, вердикт.

```text
─ /regression full ──────────────────────────
[✅] Pint           0   1.8s
[✅] ESLint         0   3.1s
[✅] Prettier       0   1.9s
[✅] vue-tsc        0   3.4s
[✅] markdownlint   0   2.0s
[✅] cspell         0   3.2s
[❌] Larastan       1   8.4s
[✅] Pest           0   71.2s
[✅] Vitest         0   12.6s
[✅] Vite build     0   2.0s
[✅] lychee         0   9.1s
[✅] gitleaks       0   4.3s
──────────────────────────────────────────────
Canonical: Pest 742/739/3sk/0 / Vitest 92f/774/3sk/0 / Vite build 2.0s / Larastan 2 /
           vue-tsc 0 / ESLint 0 / Prettier 0 / Pint 0 / markdownlint 0 / cspell 0 /
           lychee 325/0 / gitleaks 0/442
VERDICT: 🔴 RED — 1/12 failed: Larastan (см. вывод выше)
```

Каноническая строка содержит токен **каждой** проверки прогнанного уровня (`full` — 12 токенов, `quick` — 6) — это обеспечивает прозрачность «все проверки прогнаны». Формат count-токенов (по образцам из `CLAUDE.md`):

- **Pest** — `Pest <total>/<passed>/<skipped>sk/<failed>` (напр. `742/739/3sk/0`).
- **Vitest** — `Vitest <files>f/<passed>/<skipped>sk/<failed>` (напр. `92f/774/3sk/0`).
- **Vite build** — `Vite build <время>s`.
- **gitleaks** — `gitleaks <leaks>/<commits-scanned>`.
- **lychee** — `lychee <OK>/<errors>`.
- **Larastan** — `Larastan <число ошибок>` (напр. `Larastan 2`).
- **Pint / ESLint / Prettier / vue-tsc / markdownlint / cspell** — `<tool> <exit-код>` (напр. `Pint 0`); для линт/формат/тип exit-код `0` = чисто.

Вердикт:

- **🟢 GREEN** — все проверки уровня завершились exit 0; оркестратор выходит с кодом `0`.
- **🔴 RED** — ≥1 проверка с ненулевым exit; оркестратор выходит с кодом `1`; в строке вердикта перечислены все упавшие проверки.
- **🟠 RED-INCOMPLETE** — ≥1 проверка не прогналась (отсутствует бинарь — см. §8); exit-код `1`; свод нельзя признать зелёным.

Полный вывод каждой упавшей проверки оркестратор не подавляет — печатает её stdout/stderr выше таблицы, чтобы failure'ы были видны с `file:line`.

## 8. Обработка ошибок и caveats

**Отсутствие бинаря.** Если `composer`/`php`/`npm`/`bin\gitleaks.exe`/`bin\lychee.exe` не найдены — проверка помечается `[⚠] <check> SKIPPED — binary not found`, не считается провалом, но общий вердикт деградирует до `RED-INCOMPLETE`: свод неполон, зелёным признать нельзя (economy-0% — «не верифицировал X» фиксируется явно).

**Pest `--parallel` flake (квирки 72/73/77).** Известный intermittent-flake: Redis `supplier:session` race в subdir-only прогонах, cumulative state в долгих сессиях, unique-key collision в bulk-action тестах. `SKILL.md` несёт caveat: если Pest показал 1–3 ошибки, похожие на эти паттерны, — перепрогнать `composer test:parallel` один раз ИЛИ свериться с агентом `pest-parallel-debugger` до объявления реального RED. Оркестратор сам ретраи не делает (v1) — он лишь честно репортит exit-код; интерпретация flake — на человеке/Claude по caveat.

**ruflo daemon (квирк 93).** ruflo daemon worker-jitter усиливает частоту Pest-flake. `SKILL.md` несёт caveat: перед baseline-критичным `full`-прогоном рассмотреть `pm2 stop ruflo-daemon` (ruflo не трогает Redis, лишь timing-amplifier).

**Падение самого оркестратора.** Непойманное исключение в `run.mjs` → ненулевой exit + сообщение об ошибке; не выдавать за вердикт регрессии.

## 9. Инвокация и self-restraint

Frontmatter разрешает инвокацию и Claude, и пользователю (Both — Q3). Тело `SKILL.md`, раздел «Правила инвокации», фиксирует self-restraint:

- **Claude авто-запускает только `quick`** — в частности, в рамках `superpowers:verification-before-completion` перед claim «готово»/«passed»/«closed». Для этого Claude вызывает `/regression quick` (или `run.mjs quick`).
- **`full` Claude сам не запускает** — только по явному `/regression full` от пользователя ИЛИ запросив подтверждение («запускаю полный свод, ~5–10 мин — ок?»).
- **Дефолт без аргумента** — `/regression` ≡ `/regression full` (U1).
- Self-restraint — поведенческое правило в теле `SKILL.md`, не frontmatter-флаг (frontmatter не умеет различать аргументы); опирается на skill-дисциплину проекта.

## 10. Тестирование скилла

Проект тестирует `tools/*.mjs` co-located файлами `<name>.test.mjs` на встроенном `node:test` (паттерн `tools/ruflo-queen-hook.test.mjs`: unit-тесты экспортированных pure-функций + subprocess-тест `main` через `execFileSync`). `run.mjs` следует той же конвенции — TDD по `superpowers:test-driven-development`.

**`run.mjs` структурируется на два слоя:**

- **Pure-функции** (экспортируются, детерминированы, без I/O): `parse`-функция каждой проверки (вывод инструмента → count-токен), логика вердикта (массив результатов → GREEN/RED/RED-INCOMPLETE + exit-код), сборка канонической строки, платформенная развилка бинаря (`win32` → `bin\*.exe`).
- **Orchestrator** (`main`): парсинг аргумента, последовательный запуск проверок через `child_process`, печать таблицы/строки/вердикта.

**`run.test.mjs`** (co-located, `node:test` + `node:assert/strict`):

- unit-тест каждой `parse`-функции — на реальных фрагментах вывода Pest / Vitest / gitleaks / lychee / Vite build / Larastan (фикстуры-строки в тесте);
- unit-тест логики вердикта — GREEN (все exit 0), RED (≥1 ненулевой), RED-INCOMPLETE (≥1 SKIPPED) + сверка возвращаемого exit-кода;
- unit-тест сборки канонической строки (порядок токенов, число токенов = числу проверок уровня) и платформенной развилки бинаря;
- subprocess-тесты `main` через `execFileSync`: неизвестный аргумент → ошибка + ненулевой exit; `run.mjs quick` стартует и печатает заголовок уровня.

Прогон: `node --test .claude/skills/regression/run.test.mjs` (Node ≥20, нулевые зависимости).

**Дополнительная функциональная верификация** (внешние инструменты — вне unit-покрытия):

1. **Happy path.** `/regression quick` и `/regression full` на чистом дереве → каноническая строка + вердикт GREEN, exit 0.
2. **RED path.** Временный lint-лом в `.ts` → `/regression quick` даёт RED, exit 1, перечисляет ESLint; откатить лом.
3. **RED-INCOMPLETE path.** Временно недоступный бинарь (переименовать `bin\gitleaks.exe`) → `full` даёт `[⚠] SKIPPED` + RED-INCOMPLETE; вернуть бинарь.

Дисциплина `superpowers:writing-skills` — верификация скилла (unit `run.test.mjs` + функциональная 1–3) до объявления «готово».

## 11. Отклонённые альтернативы

| Альтернатива | Почему отклонена |
|---|---|
| Instruction-only `SKILL.md` (как `q-item-add`/`rls-check`) | Q2 — нет детерминизма канонической строки и гарантии полноты прогона; Claude может пропустить проверку или собрать строку в разном формате. |
| `baseline.json` со сравнением чисел | Q4 — baseline дрейфует при каждом закрытии, требует ручного `/regression baseline` ре-синка; exit-code-вердикт уже отвечает на «зелёно?». |
| Per-domain split (`/regression backend`/`frontend`/`docs`) | Q1 — нет единой «всё сразу» канонической строки без трёх отдельных вызовов. |
| Один полный прогон без уровней | Q1 — 5–10 мин на каждый вызов; нет быстрого режима для фидбэка по ходу работы. |
| PowerShell `.ps1` | U3 — проект стандартизировал tool-скрипты на Node ESM; `.ps1` не кросс-платформенный для Linux/Mac CI. |
| Параллельный запуск всех проверок | CPU-трешинг (Pest `--parallel` уже сатурирует ядра) + contention тестовых БД; YAGNI для v1. |

## 12. Нормативное соответствие

- **Создание скилла.** Реализация — терминал `superpowers:writing-skills` (`Pravila` §12.2 «Создание / правка пользовательских skills»). Текущий поток: `brainstorming` → `writing-plans` → реализация под `writing-skills`.
- **`CLAUDE.md` §5 п.10.** Скилл **не правит** `CLAUDE.md`. Он только **печатает** каноническую строку в stdout; вставка строки в `CLAUDE.md` — отдельное действие через канал `claude-md-management`.
- **Нормативный sync не требуется.** `.claude/skills/regression/` — не нормативный документ; создание скилла не требует правок `Pravila`/`CLAUDE.md`/`Tooling`/`PSR_v1`. (Опциональный follow-up — см. §13.)
- **Schema / Q-items.** Скилл не трогает `db/schema.sql` и не закрывает открытые вопросы реестра.
- **§14 ruflo Queen.** Триггер `queen`/`королева` в задаче отсутствовал; §14 не активирован. Задача (один `SKILL.md` + один скрипт) — не эпик; проактивное ruflo-предложение §14.3 неприменимо.
- **ПДн (`Pravila` §5).** Скилл запускает только проверки; gitleaks в своде сам ловит секреты. Скрипт не логирует и не персистит вывод проверок никуда, кроме stdout.

## 13. Follow-ups (вне scope v1)

- **Tooling Прил. Н.** Опционально — упомянуть `/regression` в реестре инструментов `Tooling_v8_3.md` как infrastructure/off-phase позицию. Решение — за заказчиком; вне scope v1.
- **CI-интеграция.** `run.mjs` пригоден к вызову из GitHub Actions (`node .claude/skills/regression/run.mjs full`); отдельный workflow-файл — возможный follow-up.
- **`--concurrent`-режим.** Параллельный запуск независимых doc-проверок (markdownlint/cspell/lychee) — поздняя оптимизация при необходимости.
- **Расширение свода.** Pa11y/Semgrep/knip могут быть добавлены отдельным уровнем (напр. `/regression ci`) по запросу заказчика.

---

**Истинно-открытых вопросов в этом дизайне:** 0. Все развилки согласованы (Q1–Q4) либо приняты с обоснованием и одобрены (U1–U3).
