# Srekit Runbook

> Создаёт runbook через `srekit runbook` и наполняет секции (Symptoms, Diagnose, Mitigate, Verify) из сессии. Срабатывай на «нужен runbook/playbook на алерт X», «зафиксируем, что делать при таком отказе». Runbook прескриптивный, не нарративный: императив настоящего времени для дежурного в 3 ночи; обязательны `--service` и `--alert`, без личных имён и timestamps инцидента.

- Skill: `jtprogru/srekit-runbook` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add jtprogru/srekit-runbook`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jtprogru/srekit-runbook/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: jtprogru (https://skillmd.com/u/jtprogru)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jtprogru/srekit-runbook

---


<!-- СГЕНЕРИРОВАНО bin/mirror.js. Не редактировать: правки затрёт следующая генерация.
     Источник правды — domains/<домен>/. -->

# srekit-runbook

Скаффолдит runbook через `srekit runbook` и помогает наполнить его секции **прескриптивно** (что делать в следующий раз), а не **нарративно** (что мы делали в этот раз).

## Когда использовать

- Пользователь просит написать/оформить/сделать runbook (любая формулировка).
- Пользователь только что починил что-то на проде и хочет зафиксировать процедуру для on-call.
- Пользователь упоминает алерт, под который сейчас runbook'а нет, и хочет завести.
- Известный failure mode, который надо документировать проактивно — до того, как он повторится в 3 ночи.

Не использовать:
- **Постмортем** разбираемого инцидента → навык `srekit-postmortem`.
- **Investigation log** про незакрытое расследование → команда `task` в общем навыке `srekit`.
- **RFC** про архитектурное решение → команда `rfc` в общем навыке `srekit`.

## Главный принцип: прескриптивно, не нарративно

Это **самая частая ошибка** при генерации runbook'ов из контекста сессии. Если пользователь рассказывает «мы зашли на ноду w1, увидели stale DRBD primary, сделали `drbdadm secondary`, и створки разлетелись», есть соблазн транскрибировать это как есть. Не надо. Это **incident-лог**, а не runbook. Runbook должен переписать то же знание в форме инструкции для будущего читателя.

| ❌ Нарратив (что было) | ✅ Прескрипция (что делать) |
|---|---|
| «Мы зашли на w1 и увидели stale primary» | «На каждой ноде проверь `drbdadm status \| grep -E 'Primary\|StandAlone'` — лишний Primary при отсутствии VM = stale» |
| «Алиса откатила деплой к v1.4.2» | «Откати деплой к последнему good-known тегу: `kubectl rollout undo deploy/<svc>`» |
| «После рестарта csi-controller всё разъехалось» | «Перезапусти csi-controller: `kubectl rollout restart deploy/csi-controller -n storage`. Verify: pod в `Running`, в логах нет `connection refused` в течение 60 секунд» |
| «Это случилось 3 июня в 14:25» | (вырезать; timestamps не место в runbook'е) |

Конкретные **временные отметки** и **личные имена** инцидента в runbook не попадают — это не транскрипт. Если хочешь сохранить «вот пример, как это случилось в прошлый раз» — это ссылка на постмортем под **References**, не пересказ.

## Что генерирует `srekit runbook`

CLI-команда: `srekit runbook` (требует `-T/--title`, опционально `--service`, `--alert`). По умолчанию пишет в `runbook-<slug>.md` в CWD.

Шаблон (v1 YAML-артефакт, секции по порядку):

| ID секции | Заголовок | Назначение |
|---|---|---|
| `symptoms` | Симптомы (Symptoms) | Что видит мониторинг / пользователь — конкретные сигналы |
| `severity_slo_impact` | Тяжесть и влияние на SLO (Severity & SLO impact) | Что горит, насколько срочно, будить пейджером или нет |
| `diagnose` | Диагностика (Diagnose) | Упорядоченный список проверок: команда / запрос → ожидаемый и аномальный вывод |
| `mitigate` | Смягчение (Mitigate) | Упорядоченные идемпотентные шаги. Сначала остановить «кровотечение», потом разбираться |
| `verify` | Проверка (Verify) | Конкретные критерии «починили»: алерт погас, метрика вернулась, нет повторов N минут |
| `after_the_fact` | Постфактум (After the fact) | Триггер для постмортема, напоминание обновить сам runbook тем, что удивило |
| `references` | Ссылки (References) | Дашборды, код, родственные runbook'и, прошлые постмортемы |

Frontmatter: `service`, `alert`, `tags: [runbook, oncall]`.

## Рабочий процесс

### 1. Убедись, что `srekit` доступен

```bash
srekit --version
```

Нет — попроси установить (`brew install jtprogru/tap/srekit` или `go install github.com/jtprogru/srekit@latest`), останавливайся.

### 2. Определи источник материала

Два потока:

**A. Реактивный — из реального инцидента, который пользователь только что разрулил.** Источники наполнения:
- Реплики пользователя в этой сессии: что увидел, что попробовал, что сработало.
- Если в репо лежит постмортем по этому же инциденту — прочитай (тебе нужны Mitigate-шаги и финальный root cause, не Timeline). Под References добавь ссылку.
- Логи / команды из обсуждения. Конкретные значения параметров.

**B. Проактивный — известный failure mode, который пользователь хочет задокументировать до следующего раза.** Источники:
- Реплики пользователя: «когда срабатывает X, надо проверить Y и сделать Z».
- Если есть похожий runbook в репо — посмотри его структуру (а не копируй).
- Если есть алерт-определение в `prometheus/alerts.yaml` или подобном — прочитай rule, чтобы понять что именно мониторится.

Спроси прямо одной репликой: «runbook по реальному инциденту, который только разрулили, или проактивно фиксируем известную поломку?». Это меняет, что ты у него потом будешь спрашивать.

### 3. Собери метаданные

| Поле | Откуда брать | Зачем |
|---|---|---|
| title | формулировка пользователя | Должен описывать **failure mode**, не симптом. «OOMKill of csi-controller after PVC create spike», не «CSI errors». Если разговор на русском — заголовок на русском. |
| `--service` | сервис, на который заводим | Попадает в frontmatter; помогает аукшен-фильтровать runbook'и в IDE / поиск |
| `--alert` | имя Prometheus / Alertmanager алерта | **Критично для discoverability**. Если runbook не назван так же, как алерт его не найдут в 3 ночи. Если алерта ещё нет — спроси, как он будет называться, или оставь GAP-маркер |

`--alert` — это самое часто пропускаемое поле. Если в репо есть `prometheus/`, `monitoring/`, `alerts/` — попробуй найти подходящее правило по имени, и предложи его пользователю на подтверждение.

### 4. Определи путь к файлу

Правило по приоритету:

1. **`runbooks/<slug>.md`** — если есть каталог `runbooks/` в корне репо.
2. **`docs/runbooks/<slug>.md`** — если есть `docs/runbooks/`.
3. **`runbooks/<service>/<slug>.md`** — если есть подкаталоги по сервисам внутри `runbooks/`. Распознай существующую конвенцию по `ls runbooks/`.
4. **`<slug>.md` в CWD** — последний fallback.

`<slug>` — короткий kebab-case описание failure mode: `csi-controller-oom`, `drbd-split-brain-recovery`, `kubelet-inotify-exhaustion`. Не транскрипт инцидента — общая поломка.

**Передавай `--out` явно.** Срекит-дефолт — `runbook-<slug>.md` от title. Это часто не то, что надо: в каталоге `runbooks/` префикс `runbook-` лишний, и slug от title может быть слишком длинным. Собери `--out` сам.

Если файл уже существует — **не делай `--force`, не спросив**. Возможно, это другой runbook на тот же сервис или начатый черновик.

### 5. Скаффолд через `srekit`

```bash
srekit runbook \
  -T "<title>" \
  --service "<service>" \
  --alert "<AlertName>" \
  --out "<путь из шага 4>" \
  [--templates-dir "<DIR с runbook.yaml>"]   # только если нужен проектный шаблон
```

`--templates-dir` обычно не нужен: если в `~/.config/srekit/config.yaml` указан `templates_dir`, `srekit` сам подтянет. Передавай явно, если проектный `runbook.yaml` лежит вне сконфигурированной директории.

Один вызов сразу финальный, без `--dry-run`-промежутка.

### 6. Наполни секции

После скаффолда — `Read` файла, потом `Edit` по секциям. Подробное руководство — [`references/section-guide.md`](references/section-guide.md). Краткая логика:

- **Symptoms** — что видит **мониторинг** (имя алерта, panel дашборда, паттерн в логах). Не «пользователь жалуется на медленный ответ» — это симптом для incident-доса, для runbook нужны observable сигналы.
- **Severity & SLO impact** — какой SLO горит, как быстро. Будить пейджером или дотерпит до утра. Зависимости: что ещё ломается, если этот сервис лежит.
- **Diagnose** — **упорядоченный** список проверок, от самой быстрой и информативной к более глубоким. Каждая проверка — **команда или query, которую можно вставить**, с ожидаемым и аномальным выводом. Без копи-пастабельной команды diagnose-секция бесполезна.
- **Mitigate** — **упорядоченные идемпотентные** шаги. Первый шаг — остановить кровотечение (rollback / drain / failover), а уже потом root cause. **Помечай рискованные шаги** (`> ⚠️ Перед выполнением убедись, что …`). Каждый шаг — команда + что verify после.
- **Verify** — конкретные success criteria. Чек-лист с `[ ]`. «Алерт погас», «p99 вернулся к baseline», «нет повторов N минут» — конкретные метрики и пороги, не «всё ок».
- **After the fact** — когда заводим постмортем (`SEV ≥ N`), напоминание обновить runbook тем, что в этот раз удивило, прокидывание action items.
- **References** — дашборды, код, родственные runbook'и, прошлые постмортемы по этому же failure mode.

### 7. Дисциплина

1. **Императив настоящего времени.** «Перезапусти X», «проверь Y». Не «мы перезапускаем», не «нужно перезапустить».
2. **Идемпотентность Mitigate.** Каждый шаг должен быть безопасно выполнимым повторно. Если шаг не идемпотентен — явно пометь (`> ⚠️ Выполнять ровно один раз`).
3. **Без timestamps и личных имён.** Конкретное время инцидента и кто что делал — в постмортем, не в runbook. В runbook остаются только команды, выводы, пороги.
4. **Команды копи-пастабельные.** `kubectl get pods -n storage` — да. «Посмотри статус подов» — нет. Заполни плейсхолдеры реальными namespace'ами / сервисами, если они известны; если не известны — `<placeholder>` с пояснением.
5. **Риск явно маркирован.** Любой шаг, который может усугубить или сломать другое — `> ⚠️` с указанием, что именно может сломаться.
6. **GAP-маркеры вместо галлюцинаций.** Не знаешь точную команду / порог / namespace — `<!-- GAP: уточнить … -->`, не выдумывай. Особенно опасно в Mitigate: ложный шаг приведёт к усугублению.
7. **Markdown без хардврапа.** Один абзац = одна длинная строка. Списки, таблицы, code fences — структурные переносы, как есть.
8. **Язык — как у пользователя в диалоге.** Билингвальный шаблон у срекита уже двуязычный в заголовках; основной текст пиши на языке разговора.

### 8. Прокинь discoverability (предложение, не действие)

Runbook бесполезен, если на него не наводит сам алерт. После скаффолда напомни пользователю:

- Добавить ссылку в Prometheus alert annotation: `runbook_url: <ссылка на этот файл>`.
- Если есть internal wiki / dashboard для on-call — добавить туда индексную запись.
- Если runbook покрывает алерт, для которого уже есть `runbook_url` (на старый wiki / Confluence) — обновить annotation.

Это **предложение** — пользователь сам решит, делать ли сейчас. Не патчи alerts.yaml без его явного запроса.

### 9. Сообщи пользователю

- Путь к созданному файлу.
- Оставшиеся GAP-маркеры списком (что надо досверить — namespace'ы, пороги, ссылки на дашборды).
- Напоминание прокинуть `runbook_url` в alert annotation.

Не предлагай коммит — пользователь решит сам.

## Антипаттерны

- **Транскрибировать инцидент.** «Мы сделали X, потом Y» → переформулируй в «сделай X. Если не помогло, сделай Y». Если не получается переформулировать без потери смысла — значит, runbook не место для этого знания, его место — постмортем.
- **Diagnose без команд.** «Проверь логи», «посмотри дашборд» — useless. Каждая проверка — конкретный URL дашборда или команда, которую можно скопировать.
- **Mitigate без verify-after.** Каждый шаг должен говорить, как понять, что он сработал.
- **Mitigate-шаги, которые усугубляют, если выполнить не вовремя.** Например, «перезапусти Y» без проверки, что Y не Primary — может уронить кластер. Помечай `> ⚠️`.
- **Symptoms = «пользователь жалуется на медленный ответ»** — это про incident-доска. В runbook'е Symptoms — это **алертовое событие**: какой алерт сработал, какая метрика пересекла порог.
- **Severity = «high»** без пояснения. Severity в runbook'е — это **функция от SLO impact**, не фиксированная константа. «SEV-1 если в течение часа SLI < 99%, SEV-2 если выше».
- **References с битыми ссылками.** Не вставляй ссылки, которые не верифицировал в репо / wiki. GAP-маркер лучше.
- **Не используй `git commit`/`git push`** в рамках работы скилла. Пользователь сам решает.

## Пример

Диалог: пользователь рассказывает, что нашёл stale DRBD primary на ноде w1 (`drbdadm status` показал Primary без VM), сделал `drbdadm secondary <res>`, и стейт разъехался. Хочет runbook на алерт `DRBDStalePrimary` для сервиса `linstor`.

Команда:

```bash
srekit runbook \
  -T "DRBD stale primary — demote without running VM" \
  --service linstor \
  --alert DRBDStalePrimary \
  --out runbooks/linstor/drbd-stale-primary-demote.md
```

Дальше — `Edit` секций. Mitigate (фрагмент):

```markdown
## Смягчение (Mitigate)

> ⚠️ Перед каждым шагом убедись, что на ноде **нет работающей VM**, использующей ресурс. `virsh list --all` на ноде должен показать ресурс free.

1. **Определи stale Primary:**

   ```bash
   for node in w1 w2 w3; do
     ssh $node 'drbdadm status | grep -E "Primary|StandAlone"'
   done
   ```

   Stale = Primary на ноде, где `virsh list` не показывает VM с этим ресурсом.

2. **Demote stale Primary** (идемпотентно — повторное выполнение безвредно):

   ```bash
   ssh <stale-node> 'drbdadm secondary <resource>'
   ```

3. **Verify (см. секцию Verify):** в `drbdadm status` остался ровно один Primary — на ноде с активной VM.
```

References (фрагмент):

```markdown
## Ссылки (References)

- Алерт: [`prometheus/alerts/drbd.yaml`](../../prometheus/alerts/drbd.yaml) — `DRBDStalePrimary`
- Дашборд: <!-- GAP: уточнить URL Grafana drbd-overview -->
- Прошлый постмортем (этот же failure mode): [`incidents/postmortem-2026-06-03-disk-50gb-create-hang.md`](../../incidents/postmortem-2026-06-03-disk-50gb-create-hang.md)
- Родственный runbook: [`runbooks/linstor/split-brain-resolution.md`](split-brain-resolution.md)
```

После скаффолда — напомнить пользователю прокинуть `runbook_url: <ссылка>` в Prometheus annotation для `DRBDStalePrimary`.

