Тема: Прикладная часть 11. Интеграция с реальным API: от спецификации до деплоя
Уровень сложности: Средний
Расчётное время изучения: 8-12 часов (теория 3-4 ч, практика 5-8 ч)
Предварительные требования: Прохождение частей 7-9 первого тома (цикл спецификация-план-проверка)
Часть 12 первого тома (SQLite-миграции и MVP-фаза)
Часть 16 первого тома (командное ревью кода)
Базовое владение Python 3 и командной строкой
Понимание REST API, вебхуков и формата JSON
Опыт работы с Git и базовым CI/CD
Цели обучения: Выполнить полную цепочку обработки инцидента: от сырого вебхука до нормализованного события, проверки готовности (readiness) и пробного прогона (dry-run) с корректными кодами возврата для разрешённых и запрещённых действий
Применить SDD-разделение фаз Specify/Plan/Tasks/Implement/Validate для инцидента high_memory_usage, обеспечив отсутствие конкретных команд ремедиации в спецификации
Оценить конвейер по 25-балльной модели готовности, выявить блокирующие условия (audit/stateful) и сформировать корректный пакет доказательств для capstone
Сформировать воспроизводимый след (audit trace) с привязкой всех артефактов к incident_id, пригодный для командного ревью без истории чата
Обзор: Эта глава учит строить production-ready конвейер авто-ремедиации от первого сигнала инцидента до контролируемого исполнения. В центре — учебный кейс high_memory_usage для сервиса appointments-api: нормализация вебхуков Grafana и PagerDuty, прохождение шлюза готовности по 25-балльной модели, пробный прогон заранее согласованных действий и жёсткая блокировка всего остального. Весь путь реализован в examples/real-api/ без внешних зависимостей: скрипты на стандартной библиотеке Python позволяют локально прогнать конвейер и увидеть, какие условия блокируют действие. Полный production-трек (GitOps, Kubernetes API, полный executor) отложен до накопления реплей-доказательств; в учебном минимуме достаточно доказать, что разрешённое действие проходит readiness, а запрещённое блокируется до изменения системы.
Ключевые концепции: Sdd-цикл (specify/plan/tasks/implement/validate): Структурированный процесс, где Specify фиксирует WHY/WHAT/constraints без выбора конкретной команды ремедиации, Plan выбирает стратегию, Tasks раскладывает её в исполнимые шаги, Implement применяет изменения контролируемо, Validate проверяет результат. Защищает от преждевременной реализации и обеспечивает доказуемость каждого шага.
Нормализация вебхуков: Преобразование сырых полезных нагрузок из разных источников (Grafana, PagerDuty) в единый формат incident_event с полями service, namespace, pod, severity, window_minutes, metric_context, source_refs. Устраняет дублирование и конфликт версий одного инцидента.
25-балльная модель готовности (readiness): Оценка конвейера по пяти категориям (Spec, Implementation, Verification, Process, Security) по шкале 0-5 каждая. Порог 23/25 для auto-допуска; 20-22/25 — полуручной режим. Ни одна категория не компенсирует пробел в другой. Запрещён нулевой балл по Security при любой сумме.
Блокирующие условия: Независимые от суммы readiness факторы, которые блокируют слияние: проваленная validation (Verification ≤ 2), отсутствие отката (Security ≤ 2), неопределённый радиус последствий (Implementation ≤ 2 без явного поля). Дополнительно: audit_trace_coverage < 1.0 и stateful=true без backup_verified=true.
Dry-run (пробный прогон): Проверка действия против списка заранее согласованных (pre-approved) операций без реальных изменений системы. Выполняется только после прохождения шлюза готовности. Разделяет разрешённые действия (restart_pod, scale_up_replicas_one) и запрещённые (delete_namespace).
Audit trace (журнал трассировки): Причинно-следственный след, связывающий все артефакты через incident_id: webhook_received → incident_event_normalized → /sdd:specify → spec_diff_created → commit → validate. Минимальный фрагмент воспроизводим без истории чата.
Радиус последствий: Явно указанный уровень вмешательства (pod, deployment, namespace). Защищает от непреднамеренного расширения масштаба операции. Должен быть зафиксирован до implement, не описан текстом.
Human-in-the-loop (ручное подтверждение): Обязательный элемент авто-ремедиации: человек остаётся в контуре при неопределённости, расширении радиуса последствий или повторном провале. Полностью автоматизированная ремедиация без human-review остаётся фронтир-сценарием.
Идемпотентность: Свойство задач, при котором повторный запуск не ломает состояние системы. Обязательное требование для Implementation на максимальный балл.
Gitops-фиксация: Фиксация всех изменений через Git с последующей синхронизацией (например, через ArgoCD). В учебном минимуме — справочное понятие; в полном треке — обязательный шаг перед production-переключением.
Практические упражнения: Название: Нормализация и верификация вебхука
Проблема: Запустите скрипт нормализации для фикстур Grafana и PagerDuty. Убедитесь, что выход совпадает с эталоном поле-в-поле. Затем модифицируйте webhook_grafana.json: измените memory_percent с 93 на 87 и window с 10m на 5m. Снова запустите нормализатор и объясните, почему выход всё ещё валиден, но эталон не совпадает. Какие поля критичны для идентификации инцидента, а какие — для контекста метрики?
Решение: 1. cd book2/examples/real-api
python3 scripts/normalize_webhook.py --grafana fixtures/webhook_grafana.json --pagerduty fixtures/webhook_pagerduty.json --expected fixtures/incident_event.expected.json→ ожидаем код 0.- Копируем фикстуру:
cp fixtures/webhook_grafana.json fixtures/webhook_grafana_modified.json - Меняем
memory_percentна 87 иwindowна5mв копии. - Запускаем с модифицированной фикстурой: скрипт вернёт код 1, так как выход не совпадает с
incident_event.expected.json. - Анализ:
incident_key,service,namespace,pod— критичны для идентификации (должны совпадать у обоих источников).memory_percent,window_minutes— контекст метрики, они влияют на specify, но не ломают нормализацию как процесс. Несовпадение с эталоном ожидаемо: эталон фиксирован для конкретного инцидента, а модификация создаёт другое событие.
Сложность: beginner
Название: Прогон шлюза готовности с анализом блокеров
Проблема: Последовательно прогоните три фикстуры readiness: readiness_pass.json, readiness_block_audit.json, readiness_block_stateful.json. Для каждой зафиксируйте код возврата, итоговую сумму и конкретную причину блокировки. Затем вручную создайте четвёртую фикстуру readiness_block_implementation.json с суммой 24/25, но явным radius_of_impact: 'cluster' без поля dry_run_verified: true. Предскажите и проверьте поведение шлюза.
Решение: 1. python3 scripts/check_readiness.py --readiness fixtures/readiness_pass.json → код 0, PASS incident=HM-2026-05-17-01 score=24/25.
python3 scripts/check_readiness.py --readiness fixtures/readiness_block_audit.json→ код 1,BLOCKс причинами:score=22/25 below threshold 23иaudit_trace_coverage=0.7 < 1.0.python3 scripts/check_readiness.py --readiness fixtures/readiness_block_stateful.json→ код 1,BLOCKс причиной:stateful workload without verified backup(сумма 24/25 не спасает).- Создаём
fixtures/readiness_block_implementation.jsonна базеreadiness_pass.json, меняемradius_of_impactна'cluster'и убираемdry_run_verified. - Предсказание: код 1, блокировка по
Implementation ≤ 2(неопределённый радиус последствий без dry-run) — это блокирующее условие независимо от суммы. - Проверка запуском подтверждает предсказание.
Сложность: intermediate
Название: Полный цикл specify → dry-run с запрещённым действием
Проблема: Прочитайте specs/high_memory_usage/specify.md. Убедитесь, что он содержит WHY/WHAT/constraints и pre-approved actions, но не конкретную команду kubectl. Запустите dry_run.py для restart_pod (PASS) и delete_namespace (BLOCK). Затем добавьте в spec третье действие scale_up_replicas_one с условием requires: ['stateless', 'hpa_enabled'] и создайте фикстуру, где stateless: false. Проверьте, что новое действие блокируется условием, а не только отсутствием в списке.
Решение: 1. Читаем specs/high_memory_usage/specify.md: проверяем наличие WHY, WHAT, constraints, списка pre-approved actions: [restart_pod, scale_up_replicas_one].
- Убеждаемся, что нет строки вида
kubectl delete podили конкретного API-вызова. python3 scripts/dry_run.py --spec specs/high_memory_usage/specify.md --action restart_pod→ код 0,PASS.python3 scripts/dry_run.py --spec specs/high_memory_usage/specify.md --action delete_namespace→ код 1,BLOCK: action not in pre-approved list.- Редактируем spec: добавляем
scale_up_replicas_oneсrequires: ['stateless', 'hpa_enabled']. - Создаём тестовую фикстуру или передаём контекст через аргументы скрипта, где
stateless=false. - Запускаем
dry_run.py --action scale_up_replicas_oneс контекстомstateless=false→ ожидаемBLOCK: precondition 'stateless' not met. - Вывод: dry-run проверяет не только членство в списке, но и runtime-условия, что делает его более надёжным, чем простой whitelist.
Сложность: intermediate
Название: Заполнение 25-балльной рубрики для собственного кейса
Проблема: Возьмите инцидент из вашей практики (или используйте high_memory_usage). Заполните таблицу из раздела «Практика» главы: для каждой из пяти категорий укажите балл, конкретный артефакт-доказательство (файл, лог, схема) и причину снижения. Подсчитайте итог, проверьте блокирующие условия. Если итог ниже 23 или есть блокеры — сформулируйте конкретные изменения для каждой ячейки, а не общие «улучшить процесс».
Решение: Пример заполнения для high_memory_usage:
| Категория | Балл | Артефакт-доказательство | Причина снижения |
|---|---|---|---|
| Spec | 5 | specs/high_memory_usage/specify.md с WHY/WHAT/constraints и GWT | Нет |
| Implementation | 4 | tasks.md с идемпотентными шагами, но scale_up без отдельного dry-run | Одна задача меняет состояние без предварительной проверки |
| Verification | 5 | validation.md с GWT, JSON Schema, стресс-спекой, пост-метрики в двух окнах | Нет |
| Process | 5 | Лог webhook → CLI → diff → commit с incident_id=HM-2026-05-17-01 | Нет |
| Security | 4 | specify.md с ограждениями stateful, откатом, аварийным стопом | Эскалация описана текстом без формального триггера |
| Итог | 23/25 | Блокирующие: нет | Что менять: добавить dry_run_verified для ветки scale-up в tasks.md; формализовать триггер эскалации в security_policy.md |
Проверка: 23 ≥ порог, блокеры отсутствуют → production-ready с двумя полуручными ветками.
Сложность: advanced
Название: Создание воспроизводимого следа для capstone
Проблема: Выполните минимальный runnable-цикл (шаги 1-8 из главы). Зафиксируйте результат в capstone/readiness.md в формате YAML из главы. Убедитесь, что файл читается без истории чата: каждая строка должна указывать на конкретный прогон, а не на «я обсуждал это с ассистентом». Добавьте одну реально запущенную команду в capstone/validation.md.
Решение: 1. Выполняем шаги 1-8 из раздела «Минимальный учебный сценарий».
- Создаём
capstone/readiness.md:
readiness:
pass_fixture: "readiness_pass.json -> 24/25"
blockers:
- "audit_trace_coverage=0.7 blocks auto mode (fixture: readiness_block_audit.json)"
- "stateful=true without backup_verified blocks action (fixture: readiness_block_stateful.json)"
dry_run: "restart_pod PASS; delete_namespace BLOCK (scripts/dry_run.py --spec specs/high_memory_usage/specify.md)"
- Создаём
capstone/validation.md:
# Валидация readiness-шлюза
Команды, реально запущенные:
- `python3 scripts/check_readiness.py --readiness fixtures/readiness_pass.json` → код 0
- `python3 scripts/dry_run.py --spec specs/high_memory_usage/specify.md --action restart_pod` → код 0
- Проверяем: файл читается без контекста чата, содержит конкретные фикстуры, коды возврата, команды. Нет ссылок на «ассистент сказал» или «в чате обсудили».
Сложность: intermediate
Кейсы: Название: Инцидент high_memory_usage в учебном контуре: от вебхука до блокировки запрещённого действия
Сценарий: Сервис appointments-api, построенный в части 12 первого тома на SQLite, столкнулся с пиком потребления памяти. Grafana зафиксировала memory_percent=93 за окно 10 минут для пода api-7b4 в неймспейсе appointments-api. PagerDuty подтвердила критичность и привязку к сервису. Команда разворачивает конвейер авто-ремедиации на основе материалов главы 11.
Задача: Три ключевых проблемы: (1) разные источники оповещений (Grafana и PagerDuty) дают разные версии одного инцидента, что создаёт риск двойной обработки; (2) предыдущие попытки specify сразу указывали kubectl delete pod, блокируя альтернативные стратегии и мешая Plan; (3) команда не могла отличить допустимый auto-ремедиации инцидент от требующего ручного вмешательства, что приводило к перезапуску stateful-подов без бэкапа.
Решение: Команда внедрила четырёхступенчатый конвейер из главы 11: (1) normalize_webhook.py свёл Grafana и PagerDuty к единому incident_event с incident_id=HM-2026-05-17-01, удалив чувствительные поля; (2) specify был переписан в формате WHY/WHAT/constraints с pre-approved actions (restart_pod, scale_up_replicas_one), без конкретных команд; (3) шлюз готовности на 25-балльной модели с порогом 23/25 блокировал инциденты с неполным аудитом (audit_trace_coverage=0.7) и stateful без бэкапа; (4) dry_run.py проверял действия против списка заранее согласованных, блокируя delete_namespace и другие вне списка.
Результат: Локальный прогон показал: readiness_pass.json (24/25) проходит с кодом 0, restart_pod получает PASS. readiness_block_audit.json (22/25) и readiness_block_stateful.json (24/25, но без бэкапа) блокируются с конкретными причинами в stderr. delete_namespace получает BLOCK как не входящий в pre-approved. Пакет доказательств был зафиксирован в capstone/readiness.md и прошёл командное ревью в духе части 16.
Извлечённые уроки: Specify не должен выбирать конкретную команду ремедиации — это блокирует Plan и лишает гибкости
Шлюз готовности должен проверяться до dry-run, а не после: последовательность гарантирует известный радиус последствий до проверки списка действий
Блокирующие условия (audit, stateful) работают независимо от суммы баллов — 24/25 не спасёт от блокировки stateful без бэкапа
Воспроизводимый след с привязкой к incident_id важнее истории чата: ревьюер должен проверить файл, а не верить на слово
Связанные концепции: Нормализация вебхуков
25-балльная модель готовности
SDD-цикл
Dry-run
Audit trace
Блокирующие условия
Название: Опасный антипаттерн: запуск dry-run до шлюза готовности в production-команде
Сценарий: Команда с 2-летним опытом работы с SDD попыталась ускорить конвейер, запуская dry_run.py параллельно с check_readiness.py, чтобы «не терять время на ожидание». В инциденте с memory_percent=91 действие restart_pod было формально разрешено спецификацией, но audit_trace_coverage оказался 0.8 из-за пропущенного шага /sdd:specify в чате, а не в файле.
Задача: Параллельный запуск создал race condition: dry-run вернул PASS за 200 мс, readiness вернул BLOCK через 500 мс, но оркестратор уже начал подготовку к исполнению. Отсутствие полного audit trace означало, что причинно-следственная связь между вебхуком и спецификацией не была доказуема — критично для пост-инцидентного разбора и регуляторной проверки.
Решение: Инцидент был остановлен вручную оператором, который заметил расхождение в логах. Команда ввела жёсткое правило: dry_run.py запускается только при коде возврата 0 от check_readiness.py, последовательно, с явной проверкой в скрипте-обёртке. Добавили guardrail: если audit_trace_coverage < 1.0, readiness возвращает BLOCK до любого другого анализа. Пересмотрели рубрику Process: максимальный балл 5 теперь требует не просто наличия журнала, а воспроизводимого реплея по incident_id.
Результат: Конвейер замедлился на 300 мс среднем, но нулевой инцидентов с необоснованной авто-ремедиацией в течение 6 месяцев. Рубрика Process стала самой строгой в оценках: команды с 4 баллами (требующими ручной подстановки переменной для реплея) не допускались к auto-контуру. Урок был интегрирован в учебный материал главы 11 как явное предупреждение «Плохо/Хорошо».
Извлечённые уроки: Производительность конвейера никогда не должна жертвовать доказуемостью: 300 мс ожидания — ничтожная цена за предотвращение необоснованного вмешательства
Максимальный балл по Process требует воспроизводимого реплея, а не просто наличия логов
Human-in-the-loop — не баг, а фича: даже опытные команды удерживают человека в контуре на критическом пути
Guardrails должны быть независимы от основного потока: audit проверяется первым, до любого анализа рисков
Связанные концепции: Audit trace
Блокирующие условия
Process
Human-in-the-loop
Guardrails
Советы по изучению: Проходите главу последовательно, не перепрыгивая на полный production-трек: учебный минимум (шаги 1-8) даёт 80% понимания за 20% времени
Запускайте каждый скрипт вручную и фиксируйте код возврата в заметках, а не полагайтесь на память: ревьюер capstone проверяет конкретные команды
Создавайте собственные «сломанные» фикстуры (как в упражнении 2) — это лучший способ понять, почему блокирующие условия работают независимо от суммы баллов
Читайте specs/high_memory_usage/specify.md до запуска скриптов: найдите WHY, WHAT, constraints и убедитесь, что нет конкретных команд — это ключевое отличие хорошего specify
Используйте qwen -p с флагом --approval-mode plan для необязательного шага анализа (шаг из главы), но не смешивайте его вывод с обязательным пакетом readiness: план — для ревью, скрипты — для доказательств
Параллельно с главой перечитывайте части 7-9 и 16 первого тома: конвейер главы 11 — это обёртка вокруг уже известного цикла, а не его замена
Для визуального стиля: нарисуйте на бумаге цепочку вебхук → нормализация → readiness → dry-run и отметьте, где стрелка прерывается при каждом блокере
Дополнительные ресурсы: Github spec kit: https://github.com/github/spec-kit — практическая рамка фаз Specify → Plan → Tasks → Implement, на которую ссылается глава
Github spec kit quickstart: https://github.github.io/spec-kit/quickstart.html — краткое руководство по внедрению SDD-цикла
Примеры к главе (examples/real-api/): Локальный конвейер на stdlib Python без внешних зависимостей; скрипты normalize_webhook.py, check_readiness.py, dry_run.py
Часть 12 первого тома (book/part-12-mvp.md): MVP-фаза и SQLite-миграции — контекст для инцидента high_memory_usage
Часть 7 первого тома (book/part-07-feature-specification.md): Цикл спецификации — основа для /sdd:specify
Часть 16 первого тома (book/part-16-team-code-review.md): Командное ревью — куда попадает пакет readiness
Приложение d, раздел d.5 (appendix-d-threshold-calibration.md#d5-production-готовность-глава-11): Калибровка порогов readiness для разных риск-профилей
Резюме: Глава 11 учит строить воспроизводимый конвейер авто-ремедиации от вебхука до контролируемого исполнения. Учебный минимум — четырёхступенчатая цепочка вебхук → нормализация → readiness → dry-run для кейса high_memory_usage, реализованная локально без внешних зависимостей. Ключевые принципы: SDD-разделение фаз защищает от преждевременной реализации; 25-балльная модель готовности с порогом 23/25 и независимыми блокирующими условиями (audit, stateful, откат, verification) определяет допуск к auto-контуру; dry-run проверяет действия только после прохождения шлюза; human-review остаётся обязательным элементом, а не препятствием скорости. Полный production-трек (GitOps, Kubernetes, полный executor) — фронтир, требующий накопления реплей-доказательств. Практический результат первого прохода — не оркестратор, а доказательство: разрешённое действие проходит, запрещённое блокируется, след воспроизводим по incident_id.