Начни здесь

skills-devops: тридцать навыков для дежурного инженера

Практический курс по пакету @dzhechkov/skills-devops для того, кто впервые подключает навыки к своему AI-агенту. Вместе с Тимуром ты установишь пакет, поймёшь, как навык включается по фразе и где проходят его границы, разберёшь протоколы ревью, аудита безопасности, тестов и починки CI, проведёшь миграцию базы без простоя, увидишь, как гейт дашбордов перестал верить самоотчёту, разведёшь инцидент и проблему, задеплоишь репозиторий на ВМ Cloud.ru и узнаешь, что именно покрывает подпись пакета.

Знакомься: Тимур

Тимур — дежурный инженер платформенной команды из шести человек. Сети, облако и Kubernetes — его стихия, а вот навыки для AI-агента он подключает впервые. Каждую секцию он проходит на шаг впереди тебя: сначала наступает на грабли сам, потом показывает, где они лежали.

Содержание курса

1. Зачем агенту пакет навыков?

Что такое skills-devops одной фразой и откуда взялись тридцать навыков.

Ключевая мысль: пакет из 30 навыков для DevOps-задач

Тимур — дежурный инженер платформенной команды. Сети и облако он знает наизусть, а навыки для AI-агента подключает впервые. Спросил он ровно то, что сейчас думаешь ты: «зачем агенту отдельный пакет, если он и так всё умеет?»

Ответ короткий: @dzhechkov/skills-devops — это пакет из 30 навыков для DevOps-задач. Навык — это файл SKILL.md с пошаговой процедурой, разделами «когда использовать» и «когда НЕ использовать» и описанием формата результата. Агент без навыка отвечает по памяти; агент с навыком идёт по протоколу, который кто-то уже проверил на живых задачах.

Откуда взялись тридцать навыков:

  • 10 канонизированы из проекта gitlawb/openclaude-skills (лицензия MIT): pr-review, security-audit, test-writer, ci-fix, codeql-fix, database-review, debugging, frontend-implementation, git-conflict-resolve, provider-debug;
  • 20 написаны в самом хабе — от github-actions и kubernetes до incident-response, problem-management и deploy-on-cloudru-vm.

Пакет живёт на npm: страница @dzhechkov/skills-devops; исходники — в зеркале на GitHub. В package.json на момент написания курса стоит версия 0.3.17.

Компромисс: ты получаешь проверенные протоколы вместо импровизации, но платишь контекстом агента — навыки многословны. Измерено: observability/SKILL.md — 818 строк, pr-review/SKILL.md — 80. Агент загружает навык целиком, когда тот срабатывает.

💬 Просто попроси. Команды в этом курсе нужны, чтобы ты понимал, что происходит под капотом. В Claude Code достаточно сказать «проверь этот PR» — и навык pr-review включится сам. Тимур за первую неделю ни разу не набрал имя навыка руками.

2. Установка: пресет, точечный выбор, npm

Три команды из README и чем они отличаются.

Ключевая мысль: команда dz init --preset devops

Хватит вступлений — Тимур уже открыл терминал, открывай и ты.

README пакета даёт три способа установки, и это не три синонима, а три разных решения:

  1. Пресет целиком. dz init --target claude-code --preset devops — все тридцать навыков за один вызов. Тимур начал именно так.
  2. Точечный выбор. dz init --target claude-code --select pr-review,security-audit,test-writer — только названные навыки, через запятую.
  3. Пакет напрямую. npm install @dzhechkov/skills-devops — как обычная зависимость, без раскладки под платформу.

Грабли Тимура — не повторяй. Он поставил пресет целиком в проект, где нужны были только ревью и тесты, и агент стал предлагать kubernetes на каждый вопрос про контейнеры. Пресет — быстрый старт; точечный выбор — чистый проект.

Флаг --target — это платформа, под которую dz раскладывает файлы навыков. В README примеры даны для claude-code; сам dz умеет и другие цели, но это уже курс про harness-cli, а не про этот пакет.

Компромисс: пресет ставится за минуту, но тащит лишнее; точечный выбор чист, но требует знать имена навыков (их ты найдёшь в следующей секции); npm-установка даёт версию и README, но не раскладывает файлы под агента.

💬 Просто попроси. «Поставь DevOps-навыки в этот проект» → ассистент выполнит dz init --target claude-code --preset devops; «добавь только ревью PR» → --select pr-review.

3. Навык включается по фразе

Автоактивация: фраза задачи совпадает с триггерами из SKILL.md.

Ключевая мысль: триггерные фразы навыка

Пакет стоит. Тимур пишет агенту «посмотри PR #42» — и вдруг видит в ответе структурированное ревью с вердиктом и цитатами file:line. Он ничего не включал. Что произошло?

Сработала автоактивация: навыки включаются сами, когда твоя задача совпадает с их триггерными фразами. Триггеры описаны в шапке каждого SKILL.md — в поле description и в разделе «When to use». README приводит три примера:

  • «Review this PR» → pr-review;
  • «Set up Terraform modules for AWS» → terraform;
  • «Fix the failing CI pipeline» → ci-fix.

Три взгляда на одно и то же, чтобы уложилось:

  1. Общая картина. Ты называешь намерение, а не имя навыка. Имена в пакете можно вообще не помнить.
  2. По шагам. Агент читает фразу → сверяет с триггерами → загружает SKILL.md → идёт по его процедуре → отвечает в формате из раздела «Output Format».
  3. Конкретный артефакт. Триггеры навыка deploy-on-cloudru-vm в его шапке — «задеплой в cloud.ru», «deploy this repo to a VM», «дай ссылку на подключение». Русские фразы там прописаны явно.

Две команды, чтобы смотреть в каталог руками: dz info <skill-id> показывает точные триггеры и файлы навыка, dz registry search <слово> ищет навык по слову.

Компромисс: автоактивация избавляет от заучивания имён, но зависит от формулировки — фраза мимо триггеров оставит агента без навыка, и он ответит по памяти, не сказав об этом. Тимур теперь формулирует задачу словами из области: «ревью», «инцидент», «миграция».

4. Границы навыка: «когда НЕ использовать»

Каждый навык знает соседей и передаёт задачу дальше — реши за Тимура, кому.

Ключевая мысль: границы навыка: когда не использовать

У каждого навыка в пакете есть раздел «When NOT to use» — и это не отписка, а карта передач. Навык знает своих соседей и говорит, кому отдать задачу, если она не его.

Тимур сначала не поверил, что это важно. Потом попросил pr-review «найти дыры в безопасности» — и получил обычное ревью с одной строчкой про секреты. Открыл SKILL.md — а там прямо написано: «security-focused review only → use security-audit».

Несколько границ из самих файлов навыков:

  • pr-review: не для создания PR, не для починки CI (→ ci-fix), не для чисто security-ревью (→ security-audit);
  • security-audit: не для общего ревью (→ pr-review), не для одной конкретной SAST-находки (→ codeql-fix);
  • debugging: не для ошибок сборки и компиляции (→ ci-fix), не для проектирования мониторинга (→ observability);
  • observability: не для живого инцидента (→ incident-response), не для отладки конкретного бага (→ debugging);
  • problem-management: «система лежит ПРЯМО СЕЙЧАС → incident-response»; сначала восстановить, потом искать причину.

Теперь ты в роли Тимура: в упражнении три ситуации, и в каждой надо выбрать навык. Единственно верного ответа нет только в одном месте — где задача сидит на границе двух навыков; там важно объяснить, почему ты отдал её именно так.

Компромисс: границы навыка делают ответ предсказуемым, но требуют один раз прочитать разделы «When NOT to use» — иначе ты будешь спрашивать у ревьюера то, что умеет аудитор.

5. pr-review: находки по серьёзности

Десять шагов протокола ревью и три уровня: Blocker, Important, Nit.

Ключевая мысль: группировка находок по серьёзности

Первый навык, который Тимур прочитал целиком, — pr-review. Всего 80 строк, а порядок в голове наводит на неделю вперёд.

Протокол — десять шагов, и порядок в нём не случаен:

  1. Получить diff (gh pr diff <number> или git diff <base>...<head>).
  2. Понять объём: сколько файлов, сколько строк, какие области.
  3. Прочитать изменённый код в контексте — 30–50 строк вокруг каждого изменения; diff в отрыве от файла не ревьюят.
  4. Проверить каждое изменение по восьми измерениям: корректность, обработка ошибок, крайние случаи, имена, тесты, безопасность, производительность, ломающие изменения.
  5. Сгруппировать находки по серьёзности — это сердце протокола.
  6. Написать ревью: точный file:line, цитата кода, что не так и почему, конкретное исправление.
  7. Резюме PR в 2–3 предложения.
  8. Вердикт: Approve / Request changes / Comment.
  9. Проверка полноты: миграция, документация, changelog — всё, что описание обещает, а diff не доставляет.
  10. Большой PR (>600 строк или 20+ файлов) — сказать, что его надо разбить, и предложить как.

Три уровня серьёзности:

  • Blocker — чинить до слияния: баги, потеря данных, дыры безопасности, сломанные тесты;
  • Important — стоит починить до слияния: пропущенная обработка ошибок, путаные имена, нет тестов на новое поведение;
  • Nit — приятно, но не блокирует: стиль, мелкая читаемость, опечатки.

Правило, которое Тимур переписал себе на стикер: каждый комментарий обязан быть действенным. «Это можно сделать лучше» — не комментарий. «Переименуй x в userCount, потому что на строке 47 это счётчик» — комментарий.

Компромисс: группировка находок по серьёзности заставляет ревьюера решать, что реально блокирует, — это дольше, чем вывалить список; зато автор PR видит, с чего начинать, и не тонет в Nit.

6. security-audit: находка без пути эксплуатации — не находка

Восемь категорий, четыре уровня серьёзности и одно жёсткое правило.

Ключевая мысль: путь эксплуатации для каждой находки

Вопрос без лёгкого ответа, с которого Тимур начал читать security-audit: чем аудит безопасности отличается от списка страхов?

Навык отвечает одним правилом: у каждой находки должен быть путь эксплуатации. Не «здесь теоретически может быть уязвимость», а четыре конкретных поля:

  • Input trigger — какой именно ввод пошлёт атакующий;
  • Path — через какие функции он пройдёт от точки входа до опасного стока;
  • Impact — что атакующий получит: данные, привилегии, выполнение кода, отказ в обслуживании;
  • Fix — конкретное изменение кода.

Пример из самого навыка: «отправка name='; DROP TABLE users; -- в /api/users выполнит произвольный SQL, потому что значение подставляется в запрос на строке 42» — это находка. «Стоит подумать о санитизации» — нет.

Порядок работы — семь шагов: сначала границы доверия (откуда приходят чужие данные), потом обход восьми категорий — инъекции, аутентификация и авторизация, секреты, файловые операции, сетевые операции, десериализация, XXE/SSRF, зависимости. Дальше — документирование каждой находки по четырём полям, серьёзность, проверка защит фреймворка (шаблонизатор уже экранирует? CSRF-middleware есть?) и честное признание существующих защит.

Четыре уровня серьёзности: Critical — удалённое выполнение кода, обход аутентификации; High — SQL-инъекция, хранимый XSS, повышение привилегий; Medium — CSRF, открытый редирект, утечка версий; Low — отсутствующие заголовки, болтливые ошибки.

И ещё одно правило, которое Тимур не ожидал увидеть: если уязвимостей нет — сказать об этом прямо. Выдумывать находки, чтобы оправдать аудит, запрещено.

Компромисс: требование пути эксплуатации отсекает шум и делает каждую находку проверяемой, но аудитор может пропустить реальную слабость, для которой пока не сумел построить конкретный сценарий; навык честно ставит порог именно здесь.

7. test-writer: тест, который не падает, ничего не проверяет

Одиннадцать шагов, и один из них — сломать код и убедиться, что тест это заметил.

Ключевая мысль: тест должен падать на сломанном коде

Остановись на секунду и ответь себе честно: когда ты последний раз ломал код специально, чтобы посмотреть, покраснеет ли тест? Тимур ответил «никогда» — и именно поэтому седьмой шаг test-writer стал для него главным.

Навык начинается с принципа: покрытие — инструмент, а не цель. Каждый тест существует потому, что без него уйдёт конкретный баг. Не можешь назвать этот баг — удали тест.

Одиннадцать шагов, ключевые из них:

  1. Точно определить, что тестируешь: публичный API, входы, выходы, побочные эффекты.
  2. Выбрать самый узкий тип: unit для чистых функций, integration для базы и файлов, e2e — только для критичных путей вроде входа и оплаты.
  3. Перечислить случаи: счастливый путь, граничные значения, крайние случаи, ошибки, идемпотентность.
  4. Тестировать контракт, а не реализацию: утверждения о результате, а не о внутренних вызовах.
  5. Фикстуры под задачу, а не одна гигантская на всё.
  6. Проверить, что тест падает на сломанном коде. Сломай реализацию — мысленно или по-настоящему — и убедись, что тест это ловит. Тест, зелёный при любой реализации, ничего не стоит.
  7. Имя теста описывает поведение системы: «returns 401 when token is expired», а не «test auth».

Ориентиры по времени из навыка: unit-тест — до 100 мс, интеграционный — до 2 с; дольше — значит, тест проверяет слишком многое за раз.

И последнее правило, про которое легко забыть: тесты надо запустить. Навык не разрешает заявлять «проходят», не запустив.

Компромисс: проверка «падает ли тест на сломанном коде» удваивает время на каждый тест, зато отсекает самый дорогой класс тестов — те, что годами зелёные и никого не защищают.

8. ci-fix и debugging: причина, а не симптом

Красный CI: как найти настоящую строку отказа и не спрятать проблему.

Ключевая мысль: чинить причину, а не симптом

Сюрприз, с которого начал Тимур: самый быстрый способ сделать CI зелёным — и самый запрещённый в этом пакете — одна строка. || true. Или continue-on-error: true. Или --no-verify. Навык ci-fix перечисляет все три и запрещает каждую: они не чинят, они прячут.

Вместо этого — десять шагов, из которых пять решают исход:

  1. Найти настоящую строку отказа. Лог на тысячи строк; ищи Error, FAIL, exit code, panic. Первый настоящий отказ — причина, всё после него может быть каскадом.
  2. Классифицировать: сборка, тест, линтер, деплой или инфраструктура (диск, OOM, сеть). От класса зависит, кого чинить — код или окружение.
  3. Воспроизвести локально с теми же версиями инструментов.
  4. Сверить окружения. Linux против macOS, регистр в путях, версия Node из .nvmrc, TZ=UTC в CI, параллельный запуск тестов, локаль.
  5. Чинить причину, а не симптом. Тест падает из-за часового пояса — сделай его независимым от пояса, а не пропускай. Не хватает зависимости — добавь в package.json, а не в CI-скрипт. Ошибка типа — почини тип, а не ставь @ts-ignore.

Родственный навык debugging говорит то же самое про ошибки выполнения: воспроизведи → прочитай ошибку целиком (стек читают снизу вверх; код выхода 137 — OOM, 139 — segfault) → сузь бисекцией → сформулируй опровержимую гипотезу → одно изменение за раз → почини на правильном слое. Запрещены заплатки: null-проверка в каждом вызывающем, try-catch вокруг всего, «магические» повторы, тихие значения по умолчанию.

И ещё одно, что Тимур раньше не делал: проверь, не красный ли main сам по себе. Если да — отказ старше твоего PR, и чинить его в PR — трата времени.

Компромисс: чинить причину дольше, чем спрятать симптом, — иногда на часы; зато спрятанный отказ вернётся в другом месте, дальше от причины и дороже.

9. database-migration: expand-contract без простоя

Почему нельзя переименовать колонку одной командой и как это делают за шесть шагов.

Ключевая мысль: expand-contract: миграция без простоя

Тимуру прилетела задача на вид пустяковая: переименовать колонку email в email_address в таблице users. Одна строка SQL — ALTER TABLE users RENAME COLUMN email TO email_address. Он уже занёс руку. И тут навык database-migration показал ему, что будет через секунду после этой строки: старые экземпляры приложения, которые ещё не перезапущены, начнут падать — колонки email больше нет.

Основа навыка — паттерн expand-contract: миграция без простоя, где ломающее изменение никогда не делается за один шаг. Вот он в коде, ровно как в SKILL.md:

  1. EXPAND — добавить новую колонку: ALTER TABLE users ADD COLUMN email_address VARCHAR(255); (для nullable-колонки в PostgreSQL это мгновенно, без блокировки).
  2. BACKFILL — скопировать данные пачками: UPDATE users SET email_address = email WHERE email_address IS NULL; — на большой таблице по 5–10 тысяч строк за раз, с паузами.
  3. DUAL-WRITE — выкатить код, который пишет в обе колонки.
  4. SWITCH-READ — выкатить код, который читает из новой.
  5. STOP-WRITE — выкатить код, который пишет только в новую.
  6. CONTRACT — ALTER TABLE users DROP COLUMN email; — только когда ни одна версия кода её не читает.

Порядок деплоя следует из паттерна: расширение схемы — до кода, сжатие — после кода, и никогда одновременно.

Ещё три правила из навыка, которые Тимур записал:

  • индексы в PostgreSQL создают только CREATE INDEX CONCURRENTLY — обычный CREATE INDEX блокирует таблицу;
  • у каждой миграции есть план отката, написанный ДО запуска; DROP COLUMN и DROP TABLE необратимы — восстановить можно только из бэкапа;
  • миграцию тестируют на данных производственного размера: пять секунд на 100 строках превращаются в 45 минут блокировки на 50 миллионах.

Компромисс: expand-contract растягивает одно переименование на несколько деплоев и дней, зато ни одна версия приложения ни на секунду не видит схему, которой не ожидает.

10. observability: гейт открывает файл, а не верит числу

История версии 0.3.9: как один шаг из десяти сертифицировал самоотчёт и что с этим сделали.

Ключевая мысль: гейт открывает файл дашборда, а не верит числу

Посмотри сначала на схему «до» и «после» рядом с текстом, а потом читай, как Тимур сам наступил на левую половину.

Навык observability — десять шагов, и девять из них выдают запускаемый артефакт: конфиг OpenTelemetry, правило алерта, SLO в YAML. Шаг 6 — дашборды — до версии 0.3.9 выдавал только таблицу и советы по компоновке. А выходная схема требовала число panels_count. Вот что проходило валидацию с нулём построенных панелей:

{ "name": "Payments", "type": "golden_signals", "panels_count": 6 }

Детерминированный прибор сертифицировал число, которое кто-то напечатал. README называет это инверсией, ради предотвращения которой существует лестница стоимости обнаружения: гейт, что падает на испорченном артефакте, но молчит на отсутствующем — здесь он даже до «испорченного» не доходил, потому что ничего не открывал.

Что изменилось в 0.3.9:

  • поле file стало обязательным — путь к JSON дашборда, который шаг реально записал;
  • scripts/check-dashboards.mjs открывает каждый файл: валидный JSON, непустой panels, и panels_count сверяется с реальной длиной, а не принимается на веру;
  • сам шаг 6 показывает, какой файл писать, и называет три недекоративные вещи в нём: "datasource": "${DS_PROM}" — переменная-шаблон, а не литеральный uid; "unit": "s" — потому что метрика оканчивается на _seconds; sum by (le) внутри histogram_quantile — усреднять готовый квантиль по инстансам арифметически бессмысленно.

Запуск: node observability/scripts/check-dashboards.mjs <output.json> --root <repo> — коды выхода 0 PASS · 1 FAIL · 3 NOT-ESTABLISHED, и NOT-ESTABLISHED никогда не считается проходом: гейт, который не смог запуститься, обязан сказать это вслух.

Равно важно, чего гейт НЕ доказывает — README ограничивает его честно: он не проверяет правильность запросов, не знает, испускаются ли метрики вообще, и не защищает от намеренного обмана. Четыре раунда ревью были лестницей: regex по тексту обошли через \u0064, panels:[null] — через panels:[{}]. Если автору отчёта нельзя доверять, это не тот прибор.

Компромисс: гейт, который открывает файл, ловит небрежного автора («заявил шесть панелей, построил ноль») и стоит одну команду, но ложится в структурный пол — смысл панелей всё ещё судит человек или кросс-модельное ревью.

11. Инцидент, проблема, известная ошибка

Три навыка эксплуатации — incident-response, problem-management, itsm-itil — и одна цепочка тикетов.

Ключевая мысль: инцидент восстанавливает сервис, проблема убирает причину

История одного дежурства. В 14:12 Тимуру прилетает алерт: checkout отдаёт 500-е, третий раз за месяц. Что он делает первым — ищет причину или восстанавливает сервис?

Три навыка пакета отвечают на этот вопрос хором: инцидент восстанавливает сервис, проблема убирает причину. Это две разные работы в разное время.

  • incident-response — про инцидент: подтвердить, классифицировать (SEV1 — полный отказ или потеря данных, SEV2 — сломана крупная функция, SEV3 — есть обходной путь, SEV4 — косметика), собрать роли, триаж, смягчить до поиска причины (откат, флаг, масштабирование), сообщать по расписанию, и только потом — разбор причины и постмортем в течение 48 часов, без поиска виноватых.
  • problem-management — про проблему: одна проблема может быть причиной многих инцидентов. Запись проходит состояния open → under-investigation → known-error → verifying → closed (плюс parked для отложенных). Известная ошибка (known-error) — это подтверждённая с доказательствами причина ПЛЮС записанный обходной путь; без обходного пути статус остаётся «в расследовании». Закрыть можно только проверенный фикс.
  • itsm-itil — связка: четыре типа записей (инцидент, проблема, известная ошибка, изменение/RFC) как Markdown-тикеты прямо в репозитории — docs/incidents/, docs/problems/, docs/changes/ — со ссылками друг на друга.

Что чинить первым, когда проблем несколько, решает WSJF — Weighted Shortest Job First: WSJF = Cost of Delay / Job Size. Пример из навыка: известная ошибка «5xx на checkout» — стоимость задержки 20, размер работы 2, WSJF 10; «редкая гонка при ротации логов» — 8 / 8 = 1. Первой чинят первую, хотя вторая звучит страшнее.

В упражнении Тимур проходит эту цепочку от алерта до закрытия — реши за него на каждом шаге.

Компромисс: разделение инцидента и проблемы требует двух записей вместо одной и дисциплины переводить статусы; зато пять «разных отказов» перестают маскировать один незакрытый дефект, а обходной путь ждёт дежурного в известной ошибке, а не в чьей-то памяти.

12. deploy-on-cloudru-vm: от ссылки на репозиторий до URL

Пять шагов протокола и правило: успех объявляется после verify, а не по коду выхода deploy.

Ключевая мысль: cloudru-vm verify после deploy

Тимур получает от коллеги ссылку на репозиторий и просьбу: «разверни на ВМ в Cloud.ru и дай ссылку». Раньше это был вечер. С навыком deploy-on-cloudru-vm — один разговор с агентом.

Разделение труда, как его описывает сам навык: CLI cloudru-vm (MIT, Go) берёт docker-compose-проект и делает всё тяжёлое — аутентификация → создание ВМ → плавающий IP → SFTP → docker compose up -d → health-check. Навык поставляет то, чего CLI намеренно не делает: анализ репозитория, синтез compose, сбор учётных данных и честную проверку.

Пять шагов протокола:

  1. Клонировать и понять репозиторий. Есть ли рабочий compose? Только Dockerfile? Ничего — тогда определить стек по package.json / requirements.txt / go.mod, порт, переменные окружения, внешние сервисы.
  2. Обеспечить docker-compose.yml. Есть — использовать; только Dockerfile — обернуть; ничего — синтезировать оба файла. Синтезированное показать пользователю до деплоя; секреты — спросить, никогда не выдумывать.
  3. Собрать четыре учётные данные Cloud.ru: CLOUDRU_KEY_ID + CLOUDRU_SECRET, CLOUDRU_PROJECT_ID, CLOUDRU_REGION (подсказать через cloudru-vm list-zones), CLOUDRU_IMAGE_ID (через cloudru-vm list-images --json, предпочесть Ubuntu LTS). Чего нет — спросить по имени, всё сразу.
  4. Задеплоить: cloudru-vm deploy -f docker-compose.yml --json. Повторный запуск с неизменённым хэшем compose пропускает создание ВМ.
  5. Проверить честно и передать подключение. cloudru-vm status, потом cloudru-vm verify — HTTP-проверка открытых портов; если упало — cloudru-vm logs -n 50.

Правило, ради которого Тимур запомнил этот навык: успех не объявляется по коду выхода deploy. Приложение может подняться и быть сломанным — плохой env, непрошедшие миграции. Отчёт пользователю — только после verify, или ровно то, что упало, с выдержкой из логов.

В конце Тимур отдаёт коллеге три строки: App: http://<public-ip>:<port>, SSH: ssh -i .cloudru-vm/id_ed25519 user1@<public-ip> и команды status | logs -f | destroy. И напоминает: каталог .cloudru-vm/ содержит приватный ssh-ключ — в git ему не место.

Компромисс: протокол задаёт лишние вопросы (учётные данные, подтверждение синтезированного compose) и не подходит для Kubernetes-образных нагрузок — это одна ВМ с compose; зато ни один секрет не выдуман и ни один «успех» не объявлен по коду выхода.

13. Подпись, канонический источник и ярусы доверия

Три «боковых» темы README, без которых пакет нельзя обновлять спокойно.

Ключевая мысль: подпись покрывает только файлы из тарбола

Три темы, которые в README выглядят приложениями, а на деле решают, можно ли пакету доверять и как его обновлять. Тимур пропустил их при первом чтении — и через неделю получил от проверки подписи слово TAMPERED.

1. Что покрывает подпись. Манифест .dz-manifest.json пакета покрывает ровно те файлы, которые пакет ОТГРУЖАЕТ — как их сообщает npm pack — а не всё, что лежит в рабочем дереве автора. Раньше подписывались и файлы, исключённые из files[] (обычно CHANGELOG.md), и у каждого получателя проверка говорила «в манифесте есть, в пакете нет» — пакет читался как подделанный. Переподписать в любой более ранний момент не помогло бы: этих файлов в тарболе никогда не было. Содержимое при этом не менялось — изменилось только описание.

2. Канонический источник. Этот пакет — канонический источник DevOps-навыков (ADR-001 / ADR-002). Раскладка под платформу — через dz sync --canonical packages/skills-devops --project .. Запись аддитивна: существующие файлы никогда не перезаписываются без --force. Значит, твои локальные правки навыка переживут обновление — и значит, обновления не приедут, пока ты сам не скажешь --force.

3. Ярусы доверия. В шапке каждого SKILL.md есть trust_tier. У десяти канонизированных навыков и большинства оригинальных — 2 («Validated»): есть schemas/output.json, scripts/validate-config.json, evals/<skill>.yaml. У deploy-on-cloudru-vm и itsm-itil — 1 («Structured»), и у первого прямо записан путь наверх: «Run /bto-test to promote to Tier 2». Ярус — не оценка полезности, а степень проверенности.

И последнее из README, что Тимур пересказывает коллегам: 0.3.9 вышла после чистого независимого раунда — шесть раундов кросс-семейного ревью дали C, C, D, C, затем A без находок; десять дефектов в 115 строках нового гейта закрыты, каждый закреплён тестом с тем самым входом, который его вызвал. Задержку сняли потому, что условие выполнено, а не потому, что терпение кончилось.

Компромисс: подпись только по тарболу и аддитивная синхронизация делают обновление предсказуемым, но требуют от тебя знать про --force и про то, что README в разделе Status может отставать от package.json (там сейчас 0.3.17).

Частые вопросы

Как поставить пакет и как обновиться?

Впервые: dz init --target claude-code --preset devops (все 30 навыков) или --select с именами через запятую. Обновление канонического источника: dz sync --canonical packages/skills-devops --project . — запись аддитивна, существующие файлы не перезаписываются без --force. Страница пакета: https://www.npmjs.com/package/@dzhechkov/skills-devops

Мне нужен навык, но я не знаю его имени. Что делать?

Назови задачу словами предметной области — навыки включаются по триггерным фразам из шапки SKILL.md. Посмотреть каталог руками: dz registry search <слово>; посмотреть точные триггеры и файлы навыка: dz info <skill-id>.

pr-review, security-audit и codeql-fix — в чём разница?

pr-review — общее ревью PR, безопасность там одно из восьми измерений. security-audit — аудит всего изменения по восьми категориям, с путём эксплуатации у каждой находки. codeql-fix — одна конкретная SAST-находка: реальна ли она и какой минимальный фикс. Каждый навык в разделе «When NOT to use» сам отправляет тебя к соседу.

Сервис лежит прямо сейчас. Какой навык?

incident-response: восстановить сервис (откат, флаг, масштабирование) до поиска причины. Когда сервис восстановлен и инцидент повторяется — problem-management: проблема, известная ошибка с обходным путём, очередь по WSJF. itsm-itil связывает их в Markdown-тикеты в репозитории.

Что проверяет гейт check-dashboards и чего он не проверяет?

Проверяет: каждый заявленный file существует, парсится как JSON, содержит хотя бы одну панель с типом; panels_count совпадает с реальной длиной. Не проверяет: правильность запросов, наличие метрик в живом источнике, устойчивость к намеренному обману. Коды выхода: 0 PASS, 1 FAIL, 3 NOT-ESTABLISHED — и последний никогда не проход.

Какая версия пакета актуальна?

В package.json на момент написания курса — 0.3.17. Раздел Status в README пакета отстаёт (там v0.3.0), а история версии 0.3.9 с гейтом дашбордов и областью подписи описана в самом README. Смотри версию в package.json или на странице npm.

Что такое trust_tier в шапке навыка?

Степень проверенности, а не полезности. 2 «Validated» — есть schemas/output.json, scripts/validate-config.json и evals/<skill>.yaml. 1 «Structured» — навык структурирован, но валидацию до яруса 2 ещё не прошёл; у deploy-on-cloudru-vm путь наверх записан прямо в шапке.

Бесплатный интерактивный курс · aicoding.space

Лицензии шрифтов

Onest

Copyright 2021 The Onest Project Authors (https://github.com/googlefonts/onest)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

JetBrainsMono

Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.