# Testing

> Универсальный фреймворк тестирования (frontend + backend) — дотошный прогон любой задачи на тестирование с доказательной дисциплиной. Используй, когда нужно протестировать фичу/форму/билд/API/сервис, составить план тестирования, прогнать проверки, провести исследовательское или регрессионное тестирование, найти дефекты.

- Skill: `akovalion/testing-2` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add akovalion/testing-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/akovalion/testing-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: akovalion (https://skillmd.com/u/akovalion)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/akovalion/testing-2

---


Мастер-чеклист «как тестировать что угодно» — frontend/UI и backend/сервисы. Доктрина:
- **Дотошность по умолчанию.** Покрывай всё сам: happy path → негатив → границы → редкие комбинации. Глубина пропорциональна риску, но классы проверок не пропускай.
- **Доказательность.** Pass/Fail ставится ТОЛЬКО по наблюдённому артефакту (скриншот, ответ сети, лог, дамп БД). Не проверял — `Not tested`, помешали — `Blocked` с причиной. Никаких галлюцинаций и «по логике должно работать».
- **Логируй каждое отклонение сразу.** Любое расхождение с макетом/требованиями фиксируй немедленно, даже минорное (отступ, копирайт, цвет).
- **Цель — заменить ручное тестирование.** Надёжность важнее скорости; «не успел/не смог» пишем прямо.

---

## 0. Процесс (для любой задачи)

**Сбор контекста**
- Прочитать тикет целиком: описание, AC/Gherkin, комментарии, вложения, связанные задачи (blocks/relates/epic), компонент, релиз.
- Зафиксировать source of truth для каждого требования (AC → ТЗ/Confluence → Figma → прод-поведение) и приоритет при конфликте.
- Сверить Figma: версия, режим (desktop/mobile/adaptive), states (default/hover/focus/active/disabled/loading/error/empty), варианты компонентов, токены; что в макете vs что «подразумевается».
- Найти существующие ТК (в вашей TMS — Zephyr/TestRail/др.) и автотесты (в репозитории автотестов проекта): переиспользовать, выявить пробелы, не дублировать. Статусам TMS не верить слепо — сверять с живыми тестами: встречаются «Automated» без существующего автотеста и «требуется автоматизация» на давно покрытом.
- Снять прод/preprod baseline (как фича работает сейчас — для регрессии и воспроизведения багов на актуальной версии).
- Уточнить окружение: стенд, доступы, тестовые учётки/роли, фиче-флаги, состояние данных, версия билда/коммит.
- Определить интеграции и зависимости: внешние API, платёжки, авторизация, очереди — что мокается, что реально.
- Явно зафиксировать out of scope (нативные приложения, неподдерживаемые браузеры, легаси-флоу).

**Анализ требований и вопросы аналитику**
- Каждый AC → проверка; каждая проверка → ссылка на AC или явная пометка «доп. эвристика».
- Когда скоуп = конкретное ТЗ: вердикты Pass/Fail только по его пунктам; находки вне ТЗ — отдельным блоком «вне скоупа» (наблюдение/вопрос), не Fail и не дефект задачи.
- Выявить неоднозначности («должно корректно работать», нет конкретных значений, неуказанные границы, неопределённое поведение при ошибке).
- Расхождения тикет ↔ Figma ↔ прод ↔ доку — НЕ закрывать допущением, выписать вопросом.
- Зафиксировать неопределённое поведение: пустые состояния, ошибки сети/сервера, таймауты, отказ интеграции, параллельные действия, истёкшая сессия.
- Уточнить: валидации (обязательность, форматы, маски, длины, допустимые символы, клиент vs сервер, тексты ошибок); права/роли (кто видит/может, неавторизованный, без permission); локаль/форматы (язык, дата/время/валюта/числа, TZ, направление текста).
- Все вопросы — списком с пометкой блокирующий/неблокирующий; блокирующие закрыть до старта.

**Приоритизация и риск**
- Риск по областям = вероятность дефекта × влияние (деньги, безопасность, данные, репутация, частота использования).
- Фокус на изменённом коде и его blast radius, а не ровным слоем.
- Решить: что автоматизировать (стабильное, регрессоопасное) vs ручная проверка (разведка, UX, разовое, визуал).
- Выделить smoke-подмножество (критичное для быстрой проверки билда) и regress-подмножество.
- Под дедлайн договориться о глубине явно, а не молча урезать.

**План / матрица покрытия**
- Scope: что входит/нет, на каких окружениях/браузерах/вьюпортах.
- Матрица: браузеры (Chromium/WebKit/Firefox) × вьюпорты × роли × состояния данных.
- Классы проверок: функциональные (happy/negative/boundary), UI/верстка/адаптив, валидации, навигация/роутинг/deeplink, состояния (loading/empty/error/success), доступы/роли, интеграции/API, данные/персистентность; нефункциональное (перф, security) где релевантно.
- Для каждого пункта: предусловие → действие → ожидаемый результат → привязка к AC/источнику.
- Тестовые данные: валидные/невалидные/граничные, спецсимволы, длинные строки, пустые значения, разные роли/состояния аккаунта.
- Согласовать exit-критерии и формат отчёта ДО выполнения.

**Выполнение (доказательно)**
- **Масштаб прогона.** Крупную задачу (длинный многошаговый флоу, полный регресс экрана, сверка прод/тест, E2E релиза) выполнять через fan-out (`references/fan-out.md`): оркестратор последовательно ведёт браузер и собирает артефакты, затем параллельные субагенты (`Agent`) разбирают их по осям, синтез сводит находки. Мелкую (одна страница, smoke, один баг) — линейным проходом.
- **ОБЪЁМ ПРОГОНА ОЦЕНИВАЕТСЯ ДО СТАРТА, А СПОСОБ ИСПОЛНЕНИЯ СОГЛАСОВЫВАЕТСЯ.** Прежде чем начать, посчитай объём: сколько ТК, сколько шагов, сколько разрешений/браузеров. Если объём тянет на fan-out (ориентир: >10 ТК, полный регресс экрана, длинный E2E) — **предложи это пользователю явно** и назови альтернативу с оценкой. Запрет самостоятельно запускать субагентов не запрещает их ПРЕДЛАГАТЬ: решение за пользователем, вопрос стоит десяти секунд. Молча уйти в линейный проход и упереться в контекст на середине — провал планирования: прогон встанет незаконченным, а пользователь узнает об этом постфактум. Тот же принцип для любого ограничения, способного сорвать задачу на середине (нет тестовых данных, лежит стенд, недоступна интеграция, нужен доступ) — озвучивать на старте, а не когда упёрся.
  - **Линейный проход — тоже решение, и его цена считается заранее.** Один ТК ≈ чтение карточки + 2-4 браузерных вызова + скриншоты; скриншот как изображение дороже всего остального. Не помещается по прикидке — не начинай в надежде «успею», а сокращай методику осознанно и скажи об этом.
  - **При fan-out разводи агентов по РАЗНЫМ браузерным серверам** (playwright / webkit / ios / chrome-devtools и т.п.) и пиши это в промпте каждому: параллельные агенты на одном браузере дерутся за вкладку и портят друг другу прогон. Передавай агенту карту страницы (рабочие селекторы, квирки, известные дефекты) — иначе каждый заново потратит время на discovery.
  - **Прибирай за собой, и проверяй уборку по процессам.** По завершении прогона закрой браузеры (все MCP, которые открывал), сними моки (`unrouteAll`), останови поднятые серверы, удали временные файлы. Оставленные сессии висят фоном и мешают следующему прогону.
    - **В headless-режиме забытый браузер невидим — его не заметит ни пользователь, ни ты.** Поэтому уборка заканчивается не вызовом закрытия, а проверкой списка процессов по маркерам автоматизации (`--allow-pre-commit-input`, `--disable-field-trial-config`, `--inspector-pipe`): пустой вывод = чисто. Личный браузер пользователя этих флагов не имеет и под фильтр не попадает.
    - **Не у всех браузерных MCP есть закрытие браузера** (у некоторых только закрытие вкладки, а последнюю вкладку закрыть нельзя) — там уборка только через завершение процесса браузера по маркеру автоматизации или пути временного профиля, НЕ убивая сам MCP-сервер.
    - Уборка обязательна и после промежуточных прогонов внутри задачи (проверка гипотезы, воспроизведение бага), а не только в самом конце: контексты, созданные программно, живут в браузере до закрытия.
- Воспроизводить по шагам; для каждого результата — наблюдаемый артефакт (скриншот, видео, ответ сети, консоль, дамп DOM/БД).
- Различать: «работает как ожидалось» / «баг» / «вопрос к требованиям» / «не воспроизводится» — не сваливать в одно.
- **ИНСТРУМЕНТ ИЗМЕРЕНИЯ ВРЁТ ЧАЩЕ, ЧЕМ ПРОДУКТ. Селектор/регулярка/скрипт — сами объект проверки, а не источник истины.** Реальные промахи: регулярка `/Проверьте/` поймала подзаголовок «Проверка номера» вместо ошибки; поиск модалки по фразе совпал с тем же текстом в баннере и дал «модалка открыта» там, где её не было; `innerText` с переносами строк не совпал с эталоном на корректном экране; фильтр отсёк часть сообщений, и вместо шести ошибок насчиталось одно.
  - **Любой неожиданный результат — сначала проверить измеритель, потом продукт.** «Ничего не нашлось», «нашлось не то», «нашлось одно вместо шести», «элемент не найден» — это в первую очередь гипотеза о селекторе.
  - **Подтверждать глазами то, что посчитал скриптом.** Скриншот и текст на нём — арбитр; DOM-выборка доказывает уже увиденное.
  - **Совпадение по подстроке — ненадёжно.** Уникальные фразы целиком, привязка к контейнеру (искать ошибку внутри блока поля, а не по всей странице), проверка количества найденного, а не только факта. Тексты с переносами сравнивать нормализованно.
- **BLOCKED СТАВИТСЯ, КОГДА ТК НЕВОЗМОЖНО ВЫПОЛНИТЬ, А НЕ КОГДА ИЗВЕСТНО, ЧТО ОН УПАДЁТ.** Если шаги выполнимы и объект доступен — прогнать и поставить Fail с доказательством, даже если дефект уже заведён и результат предсказуем. Blocked — только когда нет доступа, данных или окружения. Ошибочный Blocked прячет ТК из прогона и создаёт ложное «непокрыто»: так два ТК на проверку статуса простояли непрогнанными, хотя падали за минуту и давали готовое доказательство для смежной команды.
- **ПЕРЕД «ЭТО ПРОВЕРИТЬ НЕЛЬЗЯ» — ИНВЕНТАРИЗАЦИЯ ТОГО, ЧТО УЖЕ ЕСТЬ.** Посмотреть рабочую папку задачи, ранее собранные артефакты, скрипты и моки, свои прошлые заметки и память проекта: нужный инструмент часто уже написан в этой же задаче. Реальный случай: ТК объявлен непроверяемым «нужен мок-сервер», а мок-сервер лежал в папке проекта, написанный неделей раньше в рамках той же задачи. Формулировка «невозможно» допустима только после проверки доступного.
- Проверять не только UI, но и сеть (статус-коды, payload, обработку 4xx/5xx, ретраи, отсутствие чувствительных данных) и персистентность (перезагрузка, повторный вход).
- Консоль держать открытой весь прогон: ошибки/ворнинги JS, 404 ресурсов, CSP/CORS.
- Состояние после действия проверять в нескольких слоях: UI ↔ сеть ↔ БД/хранилище.
- Изолировать дефект: минимальные шаги, частота (always/intermittent), окружение, билд, предусловия; при нестабильности — повторить N раз, зафиксировать частоту, не маскировать ретраем без понимания причины.
- **ВВОД ВСЕГДА РЕАЛЬНЫЙ — это дефолт, а не частный случай.** Любое значение в любое поле вводится так, как это делает пользователь: клик по полю → посимвольный набор с клавиатуры (`pressSequentially`, `keyboard.press`, `insertText`) → уход фокуса кликом или Tab. Выбор в списках — кликом по опции, чекбоксы/радио — кликом по видимому контролу, файлы — через реальный диалог. Программная установка (`fill()`, `setInputValue`, `.value=`, native setter + `dispatchEvent`, автоматизационный «type» поверх них) обходит событийный пайплайн приложения — фреймворковые `onChange`/`onBlur`, маски, дебаунсы, кастомные компоненты, коммитящие значение только кликом по опции.
  - **Оба направления ошибки одинаково опасны.** Программный ввод даёт и ложный Fail (в поле текст есть, state пуст → «required» на заполненной форме), и ложный Pass — **валидация просто не запускается, и невалидное значение выглядит принятым**. Второе хуже: так заводится несуществующий дефект, а реальный остаётся ненайденным.
  - **Программный ввод допустим ТОЛЬКО для подготовки предусловий** — быстро добить неинтересные поля, чтобы дойти до проверяемого шага. Поле, которое проверяешь прямо сейчас, заполняется реальным вводом всегда, без исключений.
  - **Уход фокуса — тоже реальное действие.** `dispatchEvent(new Event('blur'))` и `el.blur()` НЕ эквивалентны настоящему Tab или клику мимо поля: React слушает `focusout` через собственную систему событий, синтетический `blur` до обработчика не доходит. Поле уводится из фокуса **только** реальным `Tab` или кликом по другому элементу. Это отдельная ловушка: поля с live-валидацией (email, телефон, дата) на программный blur ошибку всё-таки покажут, а поля, валидируемые на blur (ФИО, латиница), — нет. Получается пёстрая картина, где часть проверок «работает», и легко решить, что метод корректный.
  - **Вердикт Pass/Fail по валидации, маске, required, границе или формату, полученный программным вводом или программным blur, недействителен.** Не «подтверждать при сомнении», а не выставлять вовсе, пока не повторил руками. Если результат зависит от способа ввода — это находка про способ ввода, а не про продукт.
  - **«Ошибки нет» — самый подозрительный результат.** Прежде чем писать «валидация отсутствует», обязательно повтори кликом + посимвольным вводом + Tab. Отсутствие ошибки почти всегда означает, что до валидатора не дошло событие, а не что валидатора нет.
  - **«ЭЛЕМЕНТА НЕТ» — ЭТО УТВЕРЖДЕНИЕ ОБО ВСЕХ СОСТОЯНИЯХ, А НЕ О ТОМ ОДНОМ, В КОТОРОМ ТЫ ПОСМОТРЕЛ.** Прежде чем писать «крестик/кнопка/подсказка/иконка не отображается», перебери состояния, в которых элемент может появляться: фокус и потеря фокуса, ховер, пустое и заполненное значение, до и ПОСЛЕ первого успешного действия (проверки, отправки, загрузки), после ошибки, разные вьюпорты. Условие показа часто дизъюнкция («после проверки ИЛИ по потере фокуса»): проверил одну ветку и не нашёл — значит не нашёл в одной ветке, а не «нет вообще». Реальный случай: крестик очистки объявили дефектом, потому что при фокусе его нет; на самом деле он появляется после первой проверки сертификата либо после блюра — обе ветки просто не проверили.
    - **Красный флаг: пользователь или разработчик говорит «оно же есть», а у тебя нет.** Это почти всегда значит, что ты смотрел в другом состоянии, а не что у них показалось. Не спорь и не настаивай на своём замере — выясни, при каких условиях видят они, и воспроизведи ИМЕННО их сценарий.
    - Описывая поведение элемента в отчёте или ТК, давай матрицу состояний, а не одну строчку: «при фокусе нет / после блюра есть / после проверки есть» — иначе следующий прогон снова заведёт ложный дефект.
  - **НАХОДКА СУБАГЕНТА — ГИПОТЕЗА, ПОКА ТЫ НЕ ПРОВЕРИЛ ЕЁ САМ.** Ты видишь вывод агента, но не путь к нему: неверный селектор, кусок картины вместо целого, неточная сверка с макетом выглядят ровно так же убедительно, как настоящий дефект. Перед тем как завести дефект или отдать находку разработчику — воспроизведи её лично своим замером. Особенно если находка звучит как «чего-то нет на экране» или «расходится с макетом» (макет открой и посмотри сам). Прошедшие проверки (Pass) переспрашивать не нужно — перепроверяется то, что пойдёт наружу.
- **МОК ПРОВЕРЯЕТ ПОВЕДЕНИЕ, А НЕ СОДЕРЖАНИЕ. То, что ты сам положил в заглушку, находкой быть не может — это эхо твоего мока, а не дефект продукта.** Вписал в `route.fulfill` тело `{"message":"Internal Server Error"}`, увидел на экране «Internal Server Error» и завёл дефект «пользователю показывают техническое сообщение» — это круговая логика: подложил данные и сам же их «обнаружил». Так заводится несуществующий дефект и тратится время команды.
  - **На заглушке проверяется только реакция приложения:** не виснет ли UI, разблокировались ли поля и кнопка, сохранились ли введённые данные, не показался ли ошибочно экран успеха, ушёл ли повторный запрос, отработал ли ретрай, появилось ли уведомление **как факт**.
  - **На заглушке НЕ проверяется:** текст сообщения, его формулировка и язык, код ошибки, формат и структура ответа, иконка и цвет — всё это ты задал сам.
  - **Тексты, коды и форматы проверяются только на реальном ответе системы** — живым запросом, воспроизведением настоящего сбоя, либо моком, который повторяет контракт (тексты взяты из документации/свагера/живого ответа, а не выдуманы). Мок с выдуманным телом для выводов о содержании непригоден.
  - **Перед заведением дефекта по любым данным на экране спроси: откуда это значение?** Если оно пришло из твоей заглушки, тестовых данных или подмены — дефекта нет. Дефект есть, только когда значение сформировала система.
- **МОК СНЯТ — ЭТО ПРОВЕРЯЕТСЯ, А НЕ ПРЕДПОЛАГАЕТСЯ. Забытый роут превращает всё последующее тестирование в фикцию.** `page.unroute(pattern)` снимает перехват ТОЛЬКО при побуквенном совпадении строки паттерна: роут, поставленный на `'**/api/wb/submit-policy'`, НЕ снимается вызовом `unroute('**/api/wb/**')` — Playwright сравнивает строки, а не покрытие URL. Мок остаётся висеть на весь сеанс, и каждый следующий прогон получает подставной ответ, принимаемый за поведение продукта.
  - **Снимать `page.unrouteAll()`** либо строго тем же паттерном, что ставил. Ставить и снимать — в одном месте, рядом.
  - **После снятия — контрольный запрос**, и только потом любые выводы: выполнить проверяемое действие и убедиться, что ответ настоящий (статус, тело, побочный эффект вроде созданной записи).
  - **КРАСНЫЙ ФЛАГ: инструмент и браузер расходятся при идентичном запросе.** curl/API-клиент даёт 200, а браузер — 5xx, payload и заголовки совпадают побайтово → в 99% случаев это твой перехват, а не дефект. **Первым делом снять все моки и повторить**, и лишь потом искать причину в продукте, прокси, CORS или сети.
  - **Не разворачивать многошаговый разбор дефекта, пока не подтверждено, что окружение чистое.** Сравнение заголовков, гипотезы про Origin и инфраструктуру — всё это впустую, если сверху висит собственная заглушка. Сначала докажи, что видишь настоящую систему.
  - **Между сценариями окружение возвращается в исходное:** снятые роуты, `setOffline(false)`, восстановленные throttling/permissions/storage. Любая незакрытая подмена «протекает» в следующие проверки и портит их незаметно.
- **Карта действий (page map) — не переизучать страницу.** Первый проход по экрану — исследование; всё найденное (рабочие селекторы, порядок кастомных контролов, эндпоинты API, DOM-квирки, baseline шума консоли стенда) сразу фиксировать в заметку прогона/проекта. Последующие проверки на том же экране выполнять по карте без повторного discovery; повторяющиеся флоу (дойти до шага N визарда) оборачивать в скрипт-хелпер и вызывать одним действием. В начале нового прогона перепроверить 1–2 ключевых селектора карты на живой странице — могли устареть.
- **Инструментарий браузерных прогонов.** Интерактивные шаги — через MCP-браузер (Playwright / Chrome DevTools MCP). Повторяющиеся флоу и скрипт-хелперы из page map — через `playwright-cli` (отдельные процессы/профили; масштабируется на параллельных агентов, §7.8). Кросс-браузер: критичные сценарии (вёрстка, скролл, дата-пикеры, фокус/ховер, файловые инпуты) прогонять минимум в двух движках — Chromium + WebKit (MCP-сервер playwright-webkit или `playwright-cli --browser webkit`; при наличии — iOS-эмуляция): заметная часть UI-багов движко-специфична и в одном Chromium не видна.
- **Обходной путь ≠ прохождение шага.** Если целевое действие ТК не выполняется штатным пользовательским способом (клик/тап/ввод) — это Fail (дефект) или Blocked, даже когда технический workaround существует (`focus()`, native setter, прямой вызов API). Workaround допустим только чтобы разблокировать ПОСЛЕДУЮЩИЕ проверки, и это явно фиксируется в отчёте; сам заблокированный шаг «зелёным» через обход не делается.
- **РЕТЕСТ ПОСЛЕ ПРАВОК — СВЕРКА С ДВУМЯ ЭТАЛОНАМИ: с макетом И с предыдущим прогоном.** Правка одного свойства двигает соседей: реальный случай — зафиксировали высоту контейнера карусели, и уехали центрирование между кнопками-стрелками и нижний отступ; оба регресса нашёл заказчик, не прогон.
  - **Диффать координаты с прошлым прогоном.** Изменившееся число (x/y/w/h), которое правкой не объясняется, — сигнал регресса, а не шум: «x был 419, стал 360» обязан быть отработан, даже если сам элемент «в порядке».
  - **Мерить ВЗАИМНОЕ расположение, а не только сам элемент:** зазоры до соседей слева/справа, равенство парных отступов, совпадение центров (элемент ↔ контейнер ↔ вьюпорт). Проверка «карточка нужного размера на месте» не ловит, что соседние кнопки теперь на разном расстоянии.
  - **Открывать глазами скриншот КАЖДОЙ проверенной ширины и состояния.** Снятый, но не открытый скриншот = непроверенное состояние; «числа сошлись» просмотр не заменяет.
  - **Скриншот элемента-контейнера обрезает выступающих потомков** (фиксированная высота, transform, отрицательные margin) — на таком снимке контент выглядит «обрезанным», хотя это артефакт съёмки. Для визуальной сверки снимать блок целиком или вьюпорт; скрин элемента — только для точечного зума.
  - **После фикса перепроверять весь затронутый узел и смежные ТК**, а не только шаг, который падал.

**Фиксация / DoD**
- Каждый ТК со статусом + доказательством: Pass (артефакт), Fail (баг + артефакт), Blocked (причина), Not tested (почему). Blocked ≠ Fail.
- Формат записи результатов в TMS (комментарии, окружение, вложения) — строго по конвенции команды, не изобретать свой; доказательства в любом случае сохраняются в артефактах прогона и сводном отчёте.
- Дефекты заведены, связаны с тикетом, severity/priority проставлены, шаги и артефакты приложены.
- Покрытие сверено с AC: каждый AC закрыт ≥1 проверкой; непокрытые — явно с причиной.
- Прогон зафиксирован: окружение, билд/коммит, браузеры/вьюпорты, дата, исполнитель.
- Регресс затронутых областей выполнен (или осознанно отложен с фиксацией риска); блокеры эскалированы; вопросы связаны.
- Новые/обновлённые ТК внесены в TMS; кандидаты на автоматизацию помечены.
- **Не терять находки при обобщении.** Любая аномалия, замеченная в ходе прогона (даже если по ходу показалась минорной или «самоустраняющейся»), обязана попасть в итог - багом или вопросом. Вывод «некритично» не даёт права умолчать: пропущенная в отчёте находка = пропущенный дефект. Особенно ложная/зависшая ошибка валидации (см. `references/common-misses`).
- **Негатив-гейт (обязательно).** Прогон НЕ Done, пока не покрыты негативные и граничные классы и не сверено с `references/common-misses`; в отчёте обязателен раздел «Негатив» с результатом по каждому классу или явной причиной пропуска. «Объект простой/навигационный» — не основание пропускать негатив: happy-path-only прогон считается неполным.
- **Done** = все неблокированные AC проверены с доказательствами, негатив/границы покрыты (или пропуск обоснован), баги заведены, отчёт и статусы ТК актуальны, остаточные риски и непокрытое перечислены честно.

---

## 1. Техники тест-дизайна

**Классы эквивалентности (EP)** — разбить вход на классы (валидные/невалидные/спец: пустое, null, пробелы). Один представитель из каждого валидного класса; КАЖДЫЙ невалидный класс отдельно (разные сообщения = разные классы). Числа: отриц./0/полож./дробные/сверх лимита. Строки: латиница/кириллица/цифры/спецсимволы/эмодзи/RTL/регистр. Перечисления: каждый вариант + вне списка. Файлы: разрешённый/запрещённый/пустой/битый. Даты: прошлое/настоящее/будущее/невалидный формат/несуществующая (31.02).

**Граничные значения (BVA) — точные границы ±1.** Для [min..max] проверить ровно: min−1, min, min+1, max−1, max, max+1 (не «маленькое/большое»). Длина строки: 0, 1, min±1, max±1. Граница на 0: −1, 0, 1 (счётчики, остатки, корзина). Деньги: 0.00, мин. платёж, мин−0.01, макс, макс+0.01, округление копеек. Дата/время: 23:59:59→00:00:00, последний день месяца, 29.02 високос/невисокос, переход через полночь/год. Пагинация: 0 элементов, ровно страница, страница+1 элемент, последняя неполная. Возраст/срок: ровно 18 (день в день), ±1 день, expiry ровно в момент.

**Таблицы решений** — для бизнес-правил с комбинациями условий. Условия × правила × ожидаемое действие; покрыть каждую значимую комбинацию (не все 2^n); включить невозможные/противоречивые (система отвергает). Применять для: скидок/тарифов/расчётов, доступа к фиче (роль × флаг × подписка × состояние), доступности submit (поле A × поле B × чекбокс), взаимоисключающих условий.

**Переходы состояний (STT)** — для сущности с жизненным циклом (черновик→модерация→опубликовано→архив→удалено). Проверить каждый разрешённый переход и КАЖДЫЙ запрещённый (событие в недопустимом состоянии → блок). Переходы по таймауту/системному событию (автоотмена, истечение сессии); действия, недопустимые в текущем состоянии (редактировать опубликованное, оплатить отменённое); циклы/возвраты; состояние после прерывания; конкурентные переходы двух пользователей.

**Pairwise / комбинаторика** — когда параметров >3 и полный перебор нереален (ОС × браузер × роль × язык × тема). Сгенерировать набор, покрывающий все пары (PICT/allpairspy); вручную добавить критичные бизнес-связки, которые pairwise может пропустить; проверить дефолты каждого параметра.

**Error guessing** — для зрелой/легаси-функциональности по слабым местам: двойной/тройной клик, отправка до завершения валидации, спецсимволы/инъекции, очень длинный ввод (10k+), вставка большого текста, пробелы/zero-width, эмодзи, autofill, медленная сеть, Back после успеха, F5 на промежуточном шаге, правка payload в DevTools в обход UI, действие с истёкшим токеном.

**Дополнительно:** причинно-следственный анализ (AND/OR/NOT между условиями, каскадные/зависимые поля); CRUD-матрица как базовый каркас для любой сущности; матрица доступов (роль × действие × ресурс) + проверка серверной защиты. **Всегда:** позитив (валидные классы, разрешённые переходы) + негатив (невалидные классы, запрещённые переходы, обход UI).

**Выбор техники:** диапазон/лимит → BVA+EP; много параметров → pairwise; комбинации условий → таблица решений; жизненный цикл → STT; зависимые условия → cause-effect; сущность с данными → CRUD; легаси → error guessing.

---

## 2. Справочники (`references/`) — обязательное чтение по типу задачи

Детальные чек-листы вынесены в `references/`. **До составления плана прочитай целиком (Read) каждый файл, релевантный задаче** — не выборочно и не по памяти; план без прочитанного справочника считается неполным.

| Файл | Когда читать | Что внутри |
|---|---|---|
| `references/frontend.md` | Любая задача с UI | Поля/формы (маски, лимиты, paste/autofill), визуал и все состояния элементов, сверка с Figma, токены, overflow, адаптив и кросс-браузер (канонические разрешения) |
| `references/backend.md` | API / сервисы / БД / интеграции | HTTP-методы и коды, схемы/контракты, пагинация, идемпотентность, PATCH, БД (целостность, транзакции, конкурентность, миграции), AuthN/AuthZ/IDOR/мультитенантность, очереди/вебхуки/cron, нагрузка и устойчивость, OWASP API Top 10 |
| `references/cross-cutting.md` | Почти всегда (фронт и бэк вместе) | Сетевые ошибки и моки, consistency UI↔Backend, сессии/storage/мультивкладки, навигация/deeplink, время/TZ/i18n, перф и консоль, security с фронта, файлы/экспорт, поиск/фильтры, платежи, аналитика |
| `references/artifacts.md` | Перед фиксацией результатов и багов | Доказательства, HAR/консоль, структура баг-репорта, severity vs priority, расхождения с макетом, трекер/TMS, сводный отчёт прогона |
| `references/common-misses.md` | Всегда — перед финальным отчётом | Чек-лист «частые пропуски»: финальная самопроверка полноты прогона |
| `references/fan-out.md` | Крупный прогон: длинный флоу, полный регресс экрана, сверка прод/тест | Как дробить дотошный прогон на параллельных субагентов по осям: сбор артефактов оркестратором → fan-out → синтез. Про способ исполнения, не класс проверок |

Минимальные наборы: UI-задача → frontend + cross-cutting (+artifacts при заведении багов); API/бэк → backend + cross-cutting; полный E2E/релиз → все. Крупный прогон / длинный флоу → дополнительно `fan-out.md` на этапе выполнения. `common-misses.md` — всегда последним, перед выводом отчёта.

---

> Применяй технику и трек по контексту задачи. Для каждой реальной задачи сверяйся с её требованиями и макетами, а не с этим списком как с истиной — список напоминает классы проверок, но не заменяет AC и source of truth.

