Тема: Прикладная часть 5. Мутационное тестирование спецификаций
Уровень сложности: Средний
Расчётное время изучения: 6-8 часов (теория: 2-3 часа, практика: 4-5 часов)
Предварительные требования: Знакомство с JSON Schema и структурой спецификаций Given/When/Then
Прохождение части 9 первого тома (факты проверки и валидация)
Прохождение части 20 первого тома (антипаттерны SDD)
Базовые навыки работы с командной строкой и Python 3
Понимание основ графовых структур (ориентированные графы, DFS)
Опыт работы с системами контроля версий (Git)
Цели обучения: Запустить детерминированный генератор мутантов с фиксированным seed и воспроизвести идентичный набор mutation_id при повторном запуске
Настроить валидаторный контур, который отклоняет мутантов на ожидаемом шаге Given/When/Then с точным диагностическим кодом
Вычислить и интерпретировать векторную метрику иммунитета (strict_reject_rate, depth_of_diagnostics, recovery_time_p95_ms) и применить пороговые значения для CI-шлюза
Связать каждый мутант с конкретным правилом JSON Schema и шагом спецификации, обеспечивая трассируемость в SDD
Сформировать минимальный ревьюируемый след в capstone/validation.md для интеграции мутационного тестирования в процесс merge
Обзор: Мутационное тестирование спецификаций — инженерная практика, находящаяся на фронтире стандартизации, которая превращает валидатор из сторожа синтаксиса в инструмент анатомической диагностики. Суть подхода: контролируемо «портим» корректную спецификацию (base_spec.json), создаём вырожденные сценарии инцидентного процесса, и требуем, чтобы валидатор поймал каждый дефект на строго определённом шаге с предсказуемым диагностическим кодом. Ключевой принцип — «один мутант — один ожидаемый отказ». Результатом является векторная метрика иммунитета из трёх компонентов: strict_reject_rate (доля строгих отказов на ожидаемом шаге), depth_of_diagnostics (полезная глубина диагностики до остановки) и recovery_time_p95_ms (время до стабильного вердикта). Практика тесно связана с подходом «сначала спецификация» (spec-first) и превращает абсурдные кейсы в регрессионный набор против будущих токсичных требований. Учебный материал опирается на runnable-пример в examples/stress-mutator/ с инцидентом appointment_latency_spike и четырьмя операторами мутации: Nullify, FutureTime, EscalationCycle, PriorityContradiction.
Ключевые концепции: Мутационное тестирование спецификаций: Техника, при которой эталонный артефакт (base_spec.json) контролируемо искажается операторами мутации, а тест-контур обязан поймать каждый дефект. Отличается от ручного создания одиночных дефектов (глава 2) массовостью и воспроизводимостью, от верификации контрпримеров (глава 4) — привязкой к каталогу операторов, от файлового арбитража (глава 8) — генерацией, а не проверкой готовых артефактов.
Метрика иммунитета (immunity score): Векторная метрика устойчивости валидатора из трёх компонентов: strict_reject_rate (доля кейсов, отклонённых строго на ожидаемом шаге), depth_of_diagnostics (полезная глубина диагностики до отказа, измеряется числом значимых шагов в stack_route), recovery_time_p95_ms (время до возвращения стабильного вердикта). Вводится как вектор, а не скаляр, чтобы избежать ситуации, когда рост строгости сопровождается слепотой диагностики или деградацией производительности.
Принцип «один мутант — один ожидаемый отказ»: Главное правило фабрики мутаций. Каждый мутант содержит ровно одно изменение и имеет фиксированный expected_failure с кодом диагностики и точкой остановки halt_before. Запрещает составные мутации, которые делают локализацию причины невозможной. Плохо: один мутант одновременно обнуляет service_id, разворачивает граф эскалации и инвертирует приоритеты. Хорошо: мутатор Nullify обнуляет только severity, expected_failure.code = EMPTY_REQUIRED_FIELD, halt_before = When:evaluate_sla_window.
Фабрика мутаций: Детерминированный мутатор поверх корректного base_spec.json. Разбирает спецификацию в AST с узлами Given/When/Then, матрицей SLA, правилами эскалации и фрагментами JSON Schema. Применяет операторы с фиксированным seed для воспроизводимости. Критична для дуэли Верификатора и Имплементора: спорный кейс можно воспроизвести, отдать обеим ролям и проверить нарушение контракта.
Операторы мутации: Nullify (обнуление поля — пустая строка, null, пустой массив), FutureTime (сдвиг временной метки в будущее относительно согласованного now), EscalationCycle (добавление обратного ребра в граф эскалации), PriorityContradiction (введение взаимно противоречивых правил приоритета). В будущих расширениях: RecursiveDependency (косвенная рекурсия между вычисляемыми полями).
Вакцинация валидаторов: Образное название обычного мутационного тестирования спецификаций. Валидатор получает контролируемо испорченные входы и обязан отклонить их на ожидаемом шаге. Превращает абсурдные кейсы в «прививку» против регрессий.
Классы вырожденных сценариев: Пустые поля (любая пустота, без которой невозможно выбрать безопасное действие: пустые строки, пустые массивы владельцев, отсутствующие severity, service_id, runbook_ref), временные аномалии (формально корректные ISO-метки с нарушенной причинностью), обратимые циклы эскалации (бесконечное переопределение владельца), рекурсивные зависимости (косвенные цепочки вычислений с потенциальным бесконечным разворачиванием).
Детерминизм и seed: Фиксированное зерно генератора (например, 20260517) гарантирует побитовую идентичность manifest.json при повторных запусках. Два прогона подряд с одинаковым порядком mutation_id — минимальный контроль качества. Без детерминизма невозможна регрессионная база и дуэль ролей.
Графовые проверки вне json schema: JSON Schema хорошо проверяет форму данных, но не выражает топологическое поведение маршрута. Для EscalationCycle валидатор строит ориентированный граф и запускает DFS с состояниями white/gray/black. Обнаружение gray-узла возвращает минимальный цикл. Для PriorityContradiction — аналогичный контроль обратимых переходов с различием кодов CYCLE_ESCALATION и PRIORITY_REVERSAL.
Временные якоря и политика max reaction lag: Три обязательных якоря: event_detected_at, event_received_at, согласованный now из контролируемого источника времени. Три кода отказа: INVALID_TIME_ANCHOR (response_timestamp в будущем — проблема входной нагрузки), NEGATIVE_RESPONSE_LAG (отрицательная задержка — проблема нормализации), STALE_INCIDENT_WINDOW (событие старше окна — проблема SLA-правила). Разные коды критичны для SDD-журнала.
Рекурсивные зависимости и max resolution depth: Отличаются от циклов отсутствием короткой петли. Типичная цепочка: owner ← priority ← blast_radius ← owner_group ← owner. Лимит разворачивания (например, 8) с трассой попыток. Превышение → RECURSION_LIMIT с цепочкой полей, а не маскировка под таймаут. Защищает LLM-исполнителя от бесконечного уточнения.
Ci-шлюз мутаций: Блокировка слияния при нарушении любого из трёх порогов: strict_reject_rate < 0.98, depth_of_diagnostics < 3, recovery_time_p95_ms > 1200. Также блокирует пропуск старого mutation_id, ухудшение диагностической глубины, превышение лимита времени. Пример: python3 scripts/ci_gate.py --strict-reject-min 0.98 --diag-depth-min 3 --recover-ms-p95 1200 --fail-on-regression.
Ревьюируемый след и sdd-доказательства: Минимальный фрагмент в capstone/validation.md: seed, список операторов, три метрики иммунитета, verdict. Полный след: mutation_id, diff спецификации, исходный и мутированный фрагменты, журнал отклонений, код диагностики, stack_route, ссылка на правило JSON Schema. Каталог out/mutations — локальный, не коммитится; источник истины — воспроизводимая команда.
Практические упражнения: Название: Проверка детерминизма генератора мутантов
Проблема: Вам дан корректный base_spec.json и скрипт mutate_specs.py с seed=20260517. Необходимо доказать, что генератор детерминирован: два прогона подряд должны дать идентичный manifest.json. Один студент запустил скрипт один раз и увидел 4 файла мутантов. Ревьюер не может проверить воспроизводимость. Исправьте процедуру и объясните, почему один прогон недостаточен.
Решение: Шаг 1: Запустите первый прогон: python3 scripts/mutate_specs.py --base base/base_spec.json --seed 20260517 --operators Nullify,FutureTime,EscalationCycle,PriorityContradiction --out out/mutations_run1. Сохраните manifest.json. Шаг 2: Удалите out/mutations_run1 и запустите точно ту же команду с out/mutations_run2. Шаг 3: Сравните через diff out/mutations_run1/manifest.json out/mutations_run2/manifest.json. Ожидание: 0 строк различий. Шаг 4: Сравните с эталоном: diff out/mutations_run1/manifest.json manifest.example.json. Ожидание: 0 строк. Шаг 5: Проверьте стабильность порядка mutation_id: jq '.mutations | map(.mutation_id)' out/mutations_run1/manifest.json должен совпадать с run2. Объяснение: один прогон не отличает детерминированный генератор от случайного шума. Только повторный запуск создаёт регрессионную базу. Без этого невозможна дуэль Верификатора/Имплементора и CI-проверка.
Сложность: beginner
Название: Локализация утечки мутанта через глубину диагностики
Проблема: Мутант Nullify обнуляет severity. Валидатор остановился на шаге Then:notify_primary_owner с кодом MISSING_OWNER вместо ожидаемого EMPTY_REQUIRED_FIELD на When:evaluate_sla_window. strict_reject_rate = 1.0 (все отклонены), но depth_of_diagnostics = 1.2 (ниже порога 3). Проанализируйте, почему это регрессия, и опишите минимальные изменения в валидаторе и expected_failures.json для восстановления порога.
Решение: Шаг 1: Диагностируйте проблему. Пустое severity протекло слишком глубоко: валидатор не проверил обязательность поля на входе (Given:incident_received) и попытался вычислить владельца по невалидным данным. Это создаёт риск ложного уведомления о неклассифицированном инциденте. Шаг 2: Добавьте guard в нормализатор: проверка severity.minLength и severity ∈ enum до этапа evaluate_sla_window. Шаг 3: Обновите fake_validator.py: при пустом severity возвращать EMPTY_REQUIRED_FIELD с halt_before = When:evaluate_sla_window, stack_route = ['schema.normalize', 'Given:incident_received', 'field.severity.check', 'halt']. Шаг 4: Проверьте expected_failures.json: для mutation_id m_20260517_nullify_855e4297f7 ожидается code=EMPTY_REQUIRED_FIELD, halt_before=When:evaluate_sla_window, depth=4. Шаг 5: Перезапустите immunity_score.py. Ожидание: strict_reject_rate остаётся 1.0, depth_of_diagnostics восстанавливается до ≥3, recovery_time_p95_ms проверяется отдельно. Урок: высокий strict_reject_rate скрывает слепую диагностику. Векторная метрика иммунитета ловит такую регрессию.
Сложность: intermediate
Название: Настройка CI-шлюза с учётом приоритетов операторов
Проблема: История пост-мортемов показывает, что 60% production-инцидентов связаны с ошибочными временными окнами, 25% — с циклами эскалации, 10% — с пустыми полями, 5% — с конфликтами приоритетов. У вас токеновый бюджет на 100 мутантов за прогон. Стандартный равномерный набор (25 на класс) не отражает историческую уязвимость. Перестройте политику отбора операторов, обоснуйте выбор и покажите, как это влияет на expected_failures.json и пороги иммунитета.
Решение: Шаг 1: Замените равномерное распределение на взвешенное по истории: FutureTime 40 мутантов, EscalationCycle 25, Nullify 20, PriorityContradiction 10, резерв 5 для новых паттернов. Шаг 2: В mutate_specs.py задайте --operator-weights FutureTime=0.40,EscalationCycle=0.25,Nullify=0.20,PriorityContradiction=0.10. Шаг 3: Обновите expected_failures.json: увеличьте детализацию временных кодов (INVALID_TIME_ANCHOR, NEGATIVE_RESPONSE_LAG, STALE_INCIDENT_WINDOW) с разными halt_before для каждого подтипа. Шаг 4: Пересмотрите пороги: при росте числа мутантов strict_reject_rate можно поднять до 0.99, depth_of_diagnostics сохранить ≥3 (но требовать ≥4 для временных аномалий), recovery_time_p95_ms снизить до 1000 за счёт оптимизации графовых проверок. Шаг 5: В ci_gate.py добавьте --weighted-fail-on-regression, который сравнивает не только глобальные метрики, но и per-operator strict_reject_rate. Шаг 6: Документируйте обоснование в validation.md: «Приоритет FutureTime повышен согласно пост-мортемам Q1-Q3 2024, см. ссылка на инциденты #INC-2047, #INC-2089». Урок: направленное fuzzing-тестирование проверяет исторически хрупкие места, а не расходует бюджет на равномерный хаос.
Сложность: advanced
Название: Трассировка рекурсивной зависимости и настройка max_resolution_depth
Проблема: Мутант RecursiveDependency создаёт цепочку: owner вычисляется из priority, priority из blast_radius, blast_radius запрашивает owner_group, owner_group требует owner. Валидатор падает с таймаутом через 30 секунд. stack_route отсутствует. Разработчик предлагает «просто увеличить таймаут до 60 секунд». Опровергните это решение, внедрите max_resolution_depth с воспроизводимой трассой и покажите запись в validation.md.
Решение: Шаг 1: Отвергните увеличение таймаута: это маскирует проблему, делает CI непредсказуемым, не даёт диагностики для исправления контракта. Шаг 2: В fake_validator.py добавьте резолвер зависимостей с отслеживанием стека разрешения: resolution_stack = [], при входе в поле — push, при выходе — pop, при повторном push того же поля — обнаружение цикла. Шаг 3: Установите max_resolution_depth = 8. При превышении возвращайте RECURSION_LIMIT с полной цепочкой: ['owner', 'priority', 'blast_radius', 'owner_group', 'owner']. Шаг 4: Добавьте в журнал трассу попыток: resolution_attempts с timestamp каждого шага. Это позволяет отличить рекурсию от просто сложной зависимости. Шаг 5: В immunity_score.py проверьте, что для RecursiveDependency-мутантов depth_of_diagnostics включает трассу разрешения (считается как len(resolution_attempts) до обнаружения). Шаг 6: Запись в validation.md: ``yaml stress_run: seed: 20260517 operators: [Nullify, FutureTime, EscalationCycle, PriorityContradiction, RecursiveDependency] recursive_guard: max_resolution_depth: 8 verdict: PASS strict_reject_rate: "1.0 >= 0.98" depth_of_diagnostics: "5 >= 3" recovery_time_p95_ms: "400 <= 1200" `` Урок: бесконечное разворачивание должно быть наблюдаемым и контролируемым, а не заменяться таймаутом.
Сложность: advanced
Кейсы: Название: Внедрение мутационного тестирования в платформу автоуправления инцидентами
Сценарий: Команда SRE-платформы (150 инженеров, 40 микросервисов) управляла инцидентами через автоматизированный конвейер с JSON Schema-валидатором на входе. После серии production-инцидентов, где пустые поля severity протекали до этапа уведомления и вызывали ложные эскалации к VP on-call, команда решила внедрить мутационное тестирование спецификаций. Базовый сценарий — appointment_latency_spike (SLA 10 минут, эскалация appointments_oncall → sre_lead).
Задача: Три ключевые проблемы: (1) Валидатор проверял синтаксис JSON, но не ловил семантические дефекты — пустая строка severity проходила minLength=0 из-за ошибки в схеме; (2) Отсутствовала воспроизводимость: ручные тест-кейсы создавались ad-hoc и терялись при обновлении спецификаций; (3) CI пропускал регрессии, потому что единственная метрика «все тесты зелёные» скрывала ухудшение диагностики — валидатор начал возвращать GENERIC_VALIDATION_FAILED вместо точных кодов, и время отладки выросло с 15 минут до 4 часов.
Решение: Команда внедрила фабрику мутаций на базе stress-mutator из учебного курса. Шаг 1: Фиксированный seed=20240715 и 4 оператора (Nullify, FutureTime, EscalationCycle, PriorityContradiction) с приоритетом FutureTime=0.45 по истории инцидентов. Шаг 2: Связка каждого мутанта с шагом Given/When/Then и правилом JSON Schema — например, Nullify(severity) → Given:incident_received, $.properties.severity.minLength. Шаг 3: Векторная метрика иммунитета с порогами: strict_reject_rate ≥ 0.98, depth_of_diagnostics ≥ 3, recovery_time_p95_ms ≤ 1200. Шаг 4: CI-шлюз, блокирующий merge при любом нарушении или пропуске старого mutation_id. Шаг 5: SDD-доказательства: mutation_id, diff, stack_route, ссылка на правило. Шаг 6: Для графовых проверок добавлен DFS с white/gray/black состояниями, для временных — три якоря и три кода отказа.
Результат: За 3 месяца: strict_reject_rate вырос с 0.71 до 0.99, depth_of_diagnostics — с 1.8 до 4.2, recovery_time_p95_ms — с 3400 до 890 мс. Количество ложных эскалаций из-за пустых полей снизилось на 94%. Время расследования инцидентов, связанных с валидацией, упало с медианы 4 часов до 25 минут. Ключевой регресс был пойман на этапе PR: разработчик изменил порядок проверок, что сдвинуло halt_before для FutureTime-мутантов с When:evaluate_sla_window на Then:notify_owner; CI-шлюз заблокировал merge, и команда откатила изменение до анализа.
Извлечённые уроки: Векторная метрика иммунитета критична: рост strict_reject_rate до 0.99 при падении depth_of_diagnostics до 1.2 был бы ложным успехом без трёхкомпонентного контроля
Детерминизм (фиксированный seed) позволяет превратить мутации в регрессионный набор: старые mutation_id становятся «вакциной» против будущих изменений
SDD-доказательства с stack_route и ссылками на JSON Schema сокращают время онбординга новых инженеров с недели до одного дня
Приоритизация операторов по истории пост-мортемов эффективнее равномерного распределения: 45% бюджета на FutureTime дало 78% покрытия исторических дефектов
Связанные концепции: Метрика иммунитета (strict_reject_rate, depth_of_diagnostics, recovery_time_p95_ms)
Принцип «один мутант — один ожидаемый отказ»
Детерминизм и seed
CI-шлюз мутаций
Графовые проверки вне JSON Schema
SDD-доказательства и ревьюируемый след
Название: Провал из-за игнорирования recovery_time в крупном релизе
Сценарий: Платформа электронной коммерции готовилась к распродажам Чёрной пятницы. Команда валидаторов добавила 50 новых операторов мутации для проверки спецификаций платёжного контура. strict_reject_rate = 0.97, depth_of_diagnostics = 3.5 — внутри порогов. CI-шлюз пропустил релиз.
Задача: recovery_time_p95_ms вырос незаметно с 800 мс до 4500 мс из-за сложных графовых проверок на циклы в платёжных маршрутах. В период пиковой нагрузки валидатор стал узким местом: 12% запросов таймаутились на валидации, каскадно вызывая ретраи и перегрузку downstream-сервисов. Команда не проверяла recovery_time при релизе, считая его «вспомогательной метрикой».
Решение: Откат релиза в течение 45 минут. Анализ показал, что DFS на графе из 200 узлов эскалации выполнялся для каждого запроса вместо кеширования топологической сортировки. Оптимизация: предварительный графовый анализ при загрузке спецификации, инкрементальная проверка при мутациях. Введение жёсткого порога recovery_time_p95_ms ≤ 1200 с блокировкой merge. Добавление алерта при превышении 800 мс за 3 прогона подряд.
Результат: После оптимизации recovery_time_p95_ms снизился до 600 мс. В Чёрную пятницу валидатор обработал 340% пиковой нагрузки без таймаутов. Ключевое изменение процесса: recovery_time стал равноправным компонентом вектора иммунитета, а не «бонусной» метрикой.
Извлечённые уроки: Трёхкомпонентный вектор иммунитета защищает от оптимизации одной метрики в ущерб другим
recovery_time критичен для production-нагрузки: «правильный» валидатор, который тормозит CI, провоцирует обходные практики и production-отказ
Инкрементальные графовые проверки эффективнее полного DFS на каждый запрос при неизменной топологии
Связанные концепции: Метрика иммунитета и recovery_time_p95_ms
CI-шлюз мутаций
Графовые проверки вне JSON Schema
Инкрементальная валидация
Советы по изучению: Проходите материал последовательно: сначала runnable-пример (шаги 1-7), затем теория ключевых идей, затем контрольные вопросы. Попытка читать теорию без запуска скриптов создаёт иллюзию понимания
Обязательно выполните повторный запуск с тем же seed и сравните manifest.json через diff. Это минимальный контроль качества, который нельзя пропускать. Зафиксируйте результат скриншотом или копией терминала
Создайте таблицу соответствия: оператор мутации → класс вырожденного сценария → шаг Given/When/Then → правило JSON Schema → код диагностики → ожидаемый halt_before. Эта таблица — основа для настройки собственного проекта
Экспериментируйте с нарушением порогов: временно измените fake_validator.py, чтобы пропустить мутанта или сдвинуть halt_before, и наблюдайте, какой компонент вектора иммунитета это ловит. Практическое понимание регрессии важнее теоретического знания
Для визуального стиля обучения: нарисуйте блок-схему из mermaid-диаграммы курса на бумаге, отмечая, где ваш проект отличается от учебного appointment_latency_spike
Для аудиального стиля: объясните принцип «один мутант — один ожидаемый отказ» коллеге или в записи голосового сообщения. Если не можете чётко сформулировать за 60 секунд — вернитесь к материалу
Для кинестетического стиля: модифицируйте base_spec.json, добавив новое поле (например, runbook_ref), создайте для него оператор Nullify, обновите expected_failures.json и прогоните полный конвейер. Ручное создание нового оператора ускоряет освоение
Используйте необязательный шаг с Qwen Code как тренировку ревью, но не как замену runnable-проверке. Сравните его вывод с вашими фактами из immunity_score.py — расхождения сигнализируют о проблеме понимания или инструмента
Ведите «журнал мутаций» в отдельном файле: для каждого прогона фиксируйте seed, операторы, три метрики, неожиданные поведения. Этот журнал станет основой capstone/validation.md и поможет отслеживать прогресс обучения
При переходе к production-треку (калибровка порогов, CI-шлюз) обратитесь к Приложению D.1, но только после уверенного прохождения минимального сценария. Преждевременное погружение в тонкости калибровки отвлекает от понимания базовых принципов
Групповое обучение: организуйте «дуэль» — один участник меняет base_spec.json или fake_validator.py, другой должен найти, какой mutation_id сломался. Это моделирует реальное взаимодействие Верификатора и Имплементора
Дополнительные ресурсы: Исходный код учебного примера stress-mutator: book2/examples/stress-mutator/ — runnable-пример с base_spec.json, скриптами мутации, валидации и расчёта иммунитета. Первичный источник для всех практических шагов
Github spec kit: https://github.com/github/spec-kit — подход «сначала спецификация» (spec-first), на который опирается вся глава. Контракт предшествует планированию и реализации кода
Часть 9 первого тома (факты проверки): ../book/part-09-feature-validation.md — дисциплина фактов Given/When/Then, без которой мутации не имеют смысла. Мутант проверяет именно факт отказа на ожидаемом шаге
Часть 20 первого тома (антипаттерны sdd): ../book/part-20-sdd-antipatterns.md — каталог классических ошибок процесса, к которым привязаны операторы мутации
Часть 11 (учебная фича /agents): ../book/part-11-second-feature-phase.md — происхождение учебного кейса appointment_latency_spike
Часть 12 (mvp и пустой текст отзыва): ../book/part-12-mvp.md — простейший пример дисциплины фактов: пустой текст отзыва обязан быть отклонён. Здесь та же логика обобщается до набора операторов
Часть 10 (goodhart-validator и ci gate.py): examples/goodhart-validator/scripts/ci_gate.py — близкий по идее пример CI-шлюза с пороговыми значениями
Приложение d.1 (калибровка порогов): appendix-d-threshold-calibration.md#d1-мутационное-тестирование-глава-5 — полный production-трек: таблица «Низкий/По умолчанию/Высокий», упражнение по сдвигу порога, сигналы для пересмотра
Документация json schema: https://json-schema.org/ — справочник по формальным ограничениям, которые дополняются графовыми и временными проверками вне схемы
Алгоритм поиска циклов в ориентированном графе (dfs white/gray/black): Классическая реализация Тарьяна или CLRS (Cormen, Leiserson, Rivest, Stein) — основа для EscalationCycle и RecursiveDependency
Резюме: Мутационное тестирование спецификаций превращает валидатор из пассивного сторожа синтаксиса в активный инструмент анатомической диагностики. Ключевые принципы: детерминированная фабрика мутаций с фиксированным seed, строгое правило «один мутант — один ожидаемый отказ», векторная метрика иммунитета из трёх компонентов (strict_reject_rate, depth_of_diagnostics, recovery_time_p95_ms), привязка каждого мутанта к шагу Given/When/Then и правилу JSON Schema, CI-шлюз с блокировкой регрессий, SDD-доказательства с mutation_id и трассой stack_route. Учебный минимум — запуск examples/stress-mutator/ с воспроизводимым manifest.json, прохождением всех мутантов через ожидаемые отказы и зелёной метрикой иммунитета. Ревьюируемый след фиксируется в capstone/validation.md как seed, операторы, три метрики и verdict. Практика направлена на создание генератора вырожденных спецификаций для реальных проектов автоуправления инцидентами, где абсурдные кейсы становятся регрессионной защитой против токсичных требований и скрытых каскадов отказа.