# Quality gates > Статус: **active**. Каноническое описание быстрых проверок репозитория. > Краткие обязательные команды остаются в `AGENTS.md`. ## Каноническая команда Запускать из корня репозитория: ```text pnpm verify ``` `verify` выполняет последовательно: 1. `pnpm --dir web check` — SvelteKit sync и `svelte-check`. 2. `pnpm --dir web test:coverage` — полный Vitest-набор вместе с coverage-гейтом. 3. `pnpm --dir web test:rules` — отдельный быстрый прогон тестов ESLint-правил. 4. `pnpm --dir web lint:all` — ESLint, Stylelint и token audit. 5. `pnpm --dir web format:check` — форматирование кода `web/`. 6. `pnpm check:docs` — форматирование Markdown. 7. `pnpm --dir web build` — production build. Полный `test` уже включает тесты lint-правил; отдельный `test:rules` в `verify` оставлен как явный быстрый диагностический этап. ## Coverage `pnpm --dir web test:coverage` считает покрытие только по `src/lib` и падает ниже порога. Scope и список исключений — в `web/vitest.config.ts`, обоснование — в `docs/testing-strategy.md`. | Метрика | Порог | Baseline | | ---------- | ----- | -------- | | statements | 90 | 91.77% | | branches | 82 | 83.3% | | functions | 72 | 74.31% | | lines | 91 | 92.53% | Порог — нижняя граница от текущего baseline, а не оценка качества: он блокирует регрессию и не требует сначала закрывать весь долг. Локальный замер занимает ~30 с, поэтому отдельного прогона в `verify` нет — `test:coverage` заменяет `test`. ## E2E E2E не входит в `verify`, потому что Playwright поднимает build и статический сервер. Locator, fixture, i18n и `test.fixme`-политика описаны в `docs/testing-strategy.md`: ```text pnpm --dir web test:e2e ``` Запускать его обязательно для изменений маршрутов, загрузки файла, результата, download, темы или локализации. Изменения runtime fixtures/загрузки файла проверяются отдельным `web/e2e/png-fixtures.spec.ts`; ручные бинарники из `tests/fixtures/` не входят в `verify`. В текущем CI это отдельный non-blocking baseline; после закрытия известных ошибок его можно сделать blocking. Локально прогон идёт с `workers: 1` и заметно медленнее: это осознанный размен, экономит память и оставляет компьютер отзывчивым. Playwright не умеет per-project лимит воркеров, а browser matrix упирается в память, а не CPU. Тяжёлые прогоны запускаются по команде пользователя — по одному, без фонового запуска и циклов повторов; детали в `docs/agent-workflow.md`. ## Быстрые подмножества | Изменение | Команда | | ---------------------------- | ------------------------------------------------------- | | Только Markdown | `pnpm check:docs` | | TypeScript/Svelte | `pnpm --dir web check` и `pnpm --dir web test` | | Lint-правило или lint-конфиг | `pnpm --dir web test:rules` и `pnpm --dir web lint:all` | | Пользовательский поток | `pnpm --dir web test:e2e` | | Fixtures/загрузка файла | `pnpm --dir web test:e2e` | | Полный локальный gate | `pnpm verify` | ## Errors и warnings - `errors` завершают соответствующий инструмент ненулевым кодом. - После Q2 ESLint запускается с `--max-warnings=0`: новые ESLint warnings блокируют gate. - Token audit оставляет unused-token сообщение неблокирующим: после Q7a пыль вычищена, решение о fail-режиме зафиксировано в Q7d — новый токен появляется вместе с потребителем, но стиль не блокирует деплой. - Резолв `var()` в `app.css` — ошибка: несуществующий токен молча роняет объявление, поэтому этот check failing, в отличие от unused. - `lint:all` сейчас чист: 0 errors, 0 i18n warnings, 0 unused tokens. - `dict-consistency` использует base-locale fallback для registry metadata и pattern-based policy для временно неиспользуемых `tools.*.params`; это не молчаливый `allowPaths`-список. ## Решения, принятые без автоматизации Здесь лежат решения «что сознательно НЕ вводим», чтобы не пересматривать их заново через полгода. Для каждого — причина и то, что пересмотрело бы решение. В `docs/backlog.md` такие пункты не дублируются: backlog живёт задачами, а не историей решений. ### Детектор дублирования разметки — не вводим Ни свой AST-правило, ни готовая зависимость (jscpd, `eslint-plugin-sonarjs`). - Измерение по 65 `.svelte` в `components/**` и `routes/**`: при пороге 5 нормализованных строк — одна осмысленная пара, при пороге 3 строки — 17 пар и все они шум (``, `{/each}`, `{#if children}`). Семейство schema-контролов делит максимум 4 строки — это законное сходство by design. - Причина: построчное сравнение Svelte-разметки принципиально ненадёжно — одинаковый контейнер с разным содержимым даёт ложное срабатывание, а намеренно общий класс — ложную тишину. Чтобы не ловить шум, порог пришлось бы поднять до 12+ строк, и тогда правило срабатывает ровно один раз. - Контроль остаётся на ревью (расхождение видно в диффе) и на поведенческих e2e-проверках; извлечение общей разметки закреплено конвенцией в `AGENTS.md`. - Пересмотреть, если появится реально «уехавшая» копипаста в компонентах или библиотека разрастётся на уровень композиций страниц. ### Линтер согласованности комментариев — не вводим Описания инструментов различаются осознанно, и ловить это текстовым правилом дороже, чем смотреть на ревью. Действенная часть выполнена вручную: все `TODO`/`FIXME` перенесены в backlog, незарегистрированных пометок в коде нет; новые запрещены правилом в `AGENTS.md`. ### Visual snapshots — не вводим `toHaveScreenshot` сравнивает страницу с PNG-baseline из прошлого коммита. При активно меняющемся визуальном языке это превращает каждый дизайн-PR в перегенерацию и ревью картинок, а baseline платформенные (снимок с Windows не совпадёт на `ubuntu-latest`). Потребность закрывают e2e-поведение, `lint:tokens` с token audit, i18n-гейт и ручной чеклист. Вернуться стоит, когда визуальный язык стабилизируется; тогда первым идёт `/kit` в двух темах, а baseline рождаются на Linux. ### Fail-режим для unused-token аудита — не вводим Остаётся предупреждением: `verify` входит в deploy-workflow, поэтому стилевой долг блокировал бы деплой сайта. Кроме того, аудит видит только ссылки `var(--x)`, и чтение токена из JS дало бы ложное срабатывание. Правило «новый токен — вместе с потребителем» живёт в `AGENTS.md`. ### Per-project `expect.timeout` — не задаём Firefox и WebKit отдают результат так же быстро, как Chromium: расхождения объясняются памятью, а не скоростью движка. Таймаут под конкретную машину — магическое число, которое придётся подстраивать на каждом раннере, поэтому `lint`/Playwright-конфиг остаётся с дефолтами, а проблема решается лимитом воркеров. ### Coverage: браузерные модули вне scope `src/lib/core/domText.ts` и `src/lib/core/io.ts` (canvas, `createImageBitmap`) исключены из coverage-гейта: в node-Vitest они не исполнимы, и их включение превращало бы метрику в шум. `registry/*` из scope НЕ исключены — низкое покрытие там означает непокрытую логику, а не ограничение окружения. Список исключений с причинами — в `docs/testing-strategy.md`. ## CI `.github/workflows/quality.yml`: - `verify` блокирует PR при typecheck, unit, rule-test, lint, format, docs или build ошибке; - `e2e` запускается отдельным job после verify и пока не блокирует PR. `.github/workflows/deploy.yml` перед production build запускает тот же `pnpm verify`, поэтому deploy не должен проходить при ошибке быстрых проверок. ### Что и когда запускается | Workflow | Триггер | Jobs | | ------------------------ | --------------------------------------- | --------------------------- | | `Deploy to GitHub Pages` | push в `main`, dispatch | `verify`, `build`, `deploy` | | `Quality` | push в `main`, `pull_request`, dispatch | `verify`, `e2e` | Пуш в `main` запускает оба workflow: deploy проверяет быстрый gate и собирает сайт, `Quality` прогоняет тот же `verify` и browser matrix. Дублирование `verify` (~1 мин) сознательное: matrix на каждый пуш стоит ~3 мин, и условные job-и ради минуты не окупаются. Browser matrix проверяется на каждом пуше, а ручной `gh workflow run quality.yml` нужен только для перепрогона старого коммита. ### Проверка результатов CI после пуша Первый реальный прогон browser matrix происходит на runner, а не на локальной машине: локально Firefox и WebKit проходят на 1 воркере, но состав, скорость и память Linux-раннера отличаются. Поэтому после пуша разработчика: 1. дождаться завершения workflow и посмотреть, какие job вообще запустились; 2. смотреть статусы **отдельных job**, а не общий conclusion прогона: у `e2e` стоит `continue-on-error: true`, поэтому прогон с упавшим e2e целиком помечается как `success` — проверено на run `36225579363` (`E2E: failure` при `conclusion: success`); 3. если `e2e` запускался и зелёный — сообщить фактические числа passed, failed, skipped; 4. если `e2e` не запускался, сказать это прямо: matrix на раннере не проверена, и её можно прогнать через `gh workflow run quality.yml`; 5. красный `e2e` только из-за ресурсов раннера фиксируется как ограничение окружения: сначала проверяется один проект и один spec, а не увеличиваются таймауты под конкретную машину. ### Измеренная стоимость прогона Замер на ручном запуске `quality.yml` (276 e2e-тестов, 4 проекта, ubuntu-latest): | Этап | Время | | ----------------- | -------- | | `verify` job | ~1 мин | | `e2e` job целиком | ~3.1 мин | | из них сами тесты | ~2.2 мин | | весь run | ~4.2 мин | Browser matrix стоит около трёх минут, поэтому её дешёво держать на каждом PR. Полный прогон локально на 1 воркере — заметно дольше (~7 мин), и именно поэтому локальные правила нагрузки описаны в `docs/agent-workflow.md`. ## Политика e2e в CI Текущее состояние: `e2e` — non-blocking baseline, запускается на каждом пуше в `main` и на каждом PR целиком (все четыре проекта matrix). Ночного разделения нет: при стоимости job около трёх минут оно не окупается, поэтому новые browser-critical спецификации просто добавляются в `browserContractTests`. `e2e` станет blocking, когда выполнятся три условия: 1. Условия matrix пересмотрены: решено, какие спецификации обязаны идти в `browserContractTests` и `mobileTests`, нужен ли `png-fixtures` вне Chromium и что делать с engine-специфичными расхождениями декодеров. 2. Накопились стабильные прогоны на раннере без падений по таймингу. 3. Вместе с `continue-on-error` снимается и сам trick с общим conclusion: иначе зелёный прогон при красном `e2e` останется возможен. Пока условия не выполнены, `e2e` остаётся non-blocking, а его статус читается по отдельному job. ## Локальная диагностика При медленном полном прогоне сначала запускай минимальную команду из таблицы, а перед ревью или merge — `pnpm verify`. После каждого шага плана результат команд и текущий baseline фиксируются в отчёте агента.