golang

Opsgenie ушёл, JSM не пришёл: как я собрал собственный incident management с AI

  • вторник, 28 июля 2026 г. в 00:00:07
https://habr.com/ru/articles/1063128/

Первая работающая beta за две календарные недели по вечерам и один вывод: AI создаёт код, но продукт создаёт harness.

Когда Atlassian объявила о завершении продаж Opsgenie, я сначала отреагировал как нормальный инженер: решил ничего не переписывать. Потом календарь напомнил, что нормальность имеет срок действия. Продажи Opsgenie прекратились 4 июня 2025 года, а поддержка завершится 5 апреля 2027 года. После этого сервис станет недоступен, а немигрированные данные будут удалены. Это не слух из рабочего чата, а официальная позиция Atlassian (условия и даты завершения Opsgenie).

Предлагаемый путь ведёт прежде всего в Jira Service Management, причём Atlassian описывает автоматизированную миграцию данных и конфигурации (официальная страница миграции). Для многих компаний это разумный маршрут. В моём случае требование было другим: сохранить существующую self-hosted Jira, не превращать замену on-call инструмента в миграцию всей сервисной модели и получить контроль над данными, интеграциями и deployment.

Я посмотрел альтернативы. Ближе всего по общей идее оказался OpsKnight: проект позиционирует себя как open-source self-hosted платформу для incident response, on-call, routing и status pages (официальный сайт OpsKnight). На бумаге соседство было почти семейным. Но мой набор требований включал автоматическое создание задач именно в нашей Jira, Slack и eXpress, прозрачную передачу L2 в L3, русский и английский интерфейс, простое развёртывание и предсказуемое поведение в небольшом внутреннем контуре. В моём тестировании OpsKnight с этим набором не совпал и не дал нужной уверенности. Это не универсальный вердикт проекту, а описание моего опыта и моей планки риска.

Тогда я произнёс фразу, после которой обычно надо сразу добавлять буфер 100 процентов:

Сделаю за неделю.

Неделя длиной в две

Рабочая beta появилась за две календарные недели. Я занимался ей по вечерам, обычно по 1-2 часа в день. То есть это не пять полных рабочих дней, не 80 часов сверхурочной разработки и не история о том, как один человек одним промптом заменил команду.

За это время получилось собрать основной путь от alert до incident, фоновую обработку, интеграции, deployment и заметную часть web-интерфейса. Потом Aegis продолжил развиваться. До коммитов, связанных с подготовкой статьи, в репозитории было 98 коммитов; 143 тестовых файла относятся уже к более позднему снимку. Это не доказательство качества первой beta, а след того, что работа шла небольшими проверяемыми изменениями.

Название Aegis появилось как рабочее. Продукт должен отвечать на три вопроса:

  1. Кто сейчас на дежурстве?

  2. Что сломалось?

  3. Кто отвечает за восстановление и что уже произошло?

Утром возникает четвёртый: почему это повторяется и сколько времени мы теряем? Для него нужны timeline и analytics, а не память самого бодрого участника ночного созвона.

Один alert как карта продукта

Список функций легко превращает статью в README. Поэтому пройдём через Aegis вместе с одним alert:

webhook -> storage -> deduplication -> routing -> incident -> Jira -> page -> acknowledge or escalation -> timeline and analytics

Допустим, Grafana сообщает, что у payments-api выросла доля ошибок. Generic webhook проверяет секрет, нормализует payload и сохраняет alert в PostgreSQL. API ставит job process_alert, дальше работает worker. Redis я сознательно не добавлял: ещё один обязательный сервис увеличил бы стоимость эксплуатации небольшой системы сильнее, чем пользу.

Worker вычисляет fingerprint по стабильным labels, например alertname и instance. Если firing alert уже связан с открытым incident внутри deduplication window, новый сигнал прикрепляется к нему. Это первая граница между приёмником webhook и продуктом: продукт управляет шумом, а не просто складирует JSON.

Если открытого incident нет, срабатывает routing. Правила проверяются по приоритету, первое совпадение определяет team и текущего on-call. Расписание учитывает rotation, overrides и timezone. В календаре мало места для оптимизма: ошибка на переходе летнего времени превращается в страницу, которую никто не получил.

Далее создаётся incident, а Jira connector заводит задачу в нужном проекте. Chat connectors отправляют page в Slack и eXpress. Ошибка одного канала не откатывает успешную доставку в другой: результат фиксируется отдельно для каждого connector. Это важная деталь incident systems. Частичный успех здесь является нормальным состоянием, а не исключением, которое удобно спрятать за общим 500.

Инженер может acknowledge incident из чата или web UI. Состояние меняется с open на acknowledged, событие попадает в timeline, escalation job больше не должна беспокоить следующего человека. Если за заданное время подтверждения нет, worker выполняет escalate_incident: повторяет page или поднимает сигнал на следующий уровень.

Когда L2 понимает, что проблема в коде или требует владельца сервиса, incident передаётся в L3 одним действием. Handoff, first response, bounce обратно, комментарии и смены состояния остаются в общей временной шкале. После resolve эти же события питают MTTA, MTTR, noise, on-call load и статистику эскалаций.

Вокруг этого пути выросли остальные части: провайдеры OIDC - Google, Slack и eXpress; чат-интеграции - Slack и eXpress; роли admin, member, viewer; поиск, saved views, export, setup wizard и две локали. Но архитектурный центр один: alert должен пройти весь маршрут и оставить проверяемый след.

Где заканчивается магия одного промпта

AI отлично пишет локально правдоподобный код. Просишь handler для acknowledge, получаешь handler. Просишь React-компонент timeline, получаешь timeline. Проблема начинается, когда они должны стать одним продуктом.

Модель не живёт внутри моей головы. Она не знает, что Jira должна создаваться до page, что частичная ошибка Slack не должна отменять eXpress, что новый UI-текст обязан одновременно появиться на двух языках, что миграции нужны вверх и вниз, а incident page считается законченной только после реального API wiring. Если не дать ей систему ограничений, она заполнит пробелы наиболее вероятными решениями. Вероятными вообще, а не правильными для Aegis.

Каждый отдельный кусок при этом выглядит разумно: endpoint компилируется, компонент рендерится, happy path проверен. Через неделю выясняется, что endpoint отвечает не по контракту, UI работает на fixtures, а два модуля по-разному понимают одно состояние.

One-shot prompting оптимизирует следующий ответ. Разработка продукта оптимизирует цепочку решений. Между ними и находится harness.

Harness для меня не специальная модель, а среда, в которой AI не может незаметно менять смысл продукта: документы, правила, короткие задачи, инструментальные проверки и явные остановки.

Из чего был собран harness Aegis

Первым появился product brief: пользователи, четыре функции MVP, north stars и non-goals. В MVP нет SMS, телефонного paging, multi-tenant SaaS, status pages, Helm и собственного identity provider. Без этого списка AI охотно строит впечатляющую платформу на все случаи жизни, то есть не тот продукт.

Дальше PRD превратил намерения в нумерованные требования. У каждого требования есть ID, поэтому story и pull request могут ссылаться не на настроение автора, а на проверяемое условие. В репозитории действует простая иерархия:

PRD важнее story
story важнее предположения агента

Если код не совпадает со спецификацией, нельзя тихо переписать документ под код. Нужно предложить изменение спецификации. Это дисциплина не для AI, а для меня: очень удобно объявить внезапную реализацию новым замыслом, особенно в 23:40.

Architecture описала границы API, worker, web и PostgreSQL, request path, job runner и connector pattern. Data model зафиксировала сущности и состояния. API contract определил endpoints, тела запросов, ошибки и callbacks. Stories нарезали объём на вертикальные изменения с acceptance criteria и зависимостями.

Design system ограничил визуальные импровизации: severity, компоненты, состояния, тексты, spacing и Storybook стали частью контракта. Иначе AI за вечер изготовит три профессиональных, но разных приложения.

Наконец, Definition of Done связал артефакты с кодом. Требование считается выполненным, когда acceptance criteria покрыты тестами, lint, type и test зелёные, API и database changes отражены в документации, миграции имеют up и down, секреты не попали в репозиторий, тексты есть на en и ru, а UI следует design system.

Главная роль этих файлов не в том, чтобы произвести документацию. Они уменьшают пространство допустимых догадок. Чем меньше агенту приходится угадывать, тем больше его скорость превращается в полезный результат.

Цикл поставки, который не даёт разбежаться

Для каждой задачи использовался один и тот же путь:

story -> short plan -> vertical slice with tests -> lint/type/test -> self-review -> PR -> merge

Story отвечает на вопрос, что должно измениться и как проверить результат. Она защищает от scope creep. Если во время реализации внезапно хочется добавить универсальный rules engine, story вежливо напоминает, что сейчас нужен один шаг эскалации.

Short plan занимает 3-8 строк: затрагиваемые файлы, подход и тесты. Его достаточно, чтобы заметить ошибочную границу до написания кода. Если план требует одновременно переписать schema, API, routing и половину UI, скорее всего, story надо разделить.

Vertical slice означает, что изменение проходит через необходимые слои и даёт наблюдаемое поведение. Для incident acknowledge это не только SQL и не только кнопка. Это state transition, handler, authorization, timeline event, UI action и тесты на допустимые и недопустимые переходы.

Тесты пишутся вместе с slice. Они ловят не только регрессии, но и разные трактовки требования. Table-driven тесты хорошо разоблачают модель, которая реализовала open -> resolved, но забыла запретить resolved -> acknowledged.

Далее идут три инструментальных gate:

  • lint ловит небезопасные и несогласованные конструкции до review;

  • type ловит несовместимые формы данных внутри Go и TypeScript до runtime, а сверка API contract не даёт двум сторонам закрепить разные трактовки;

  • test проверяет бизнес-переходы, а NFR и Definition of Done задают порог не ниже 90 процентов coverage бизнес-логики.

Сам по себе высокий coverage ничего не гарантирует. Можно покрыть тестом вызов функции и не проверить результат. Поэтому self-review проходит по acceptance criteria и Definition of Done, а не по красивой зелёной цифре.

После этого открывается PR. PR создаёт ещё одну полезную границу контекста: diff должен быть объясним другому человеку и мне завтрашнему. Merge завершает story, после чего берётся следующая Ready задача. В каждый момент Ready обычно одна. AI умеет параллелить работу, но зависимости от этого не исчезают. Девять агентов, одновременно придумавших девять вариантов data model, дают не ускорение, а очень энергичное совещание.

BMAD: не генератор кода, а система ролей

К такой схеме я пришёл не с первого коммита. Сначала harness Aegis был самодельным: документы, backlog, правила для агента и gate-команды. Позже я подключил BMAD Method и увидел более зрелую формализацию той же идеи.

BMAD организует разработку вокруг специализированных ролей и workflow. В официальной документации отдельно описаны Analyst, PM, Architect, Developer и другие агенты, каждому сопоставлены свои процессы (BMAD: список агентов и workflow). Это не попытка изобразить виртуальный штат с должностями. Роли нужны, чтобы модель в конкретный момент решала один класс задач и читала подходящий набор артефактов.

Analyst помогает пройти discovery: сформулировать проблему, пользователей, ограничения и варианты. PM превращает результат в PRD и scope. Architect принимает решения о границах компонентов, data flow и технических invariants. Затем требования разбиваются на epics и stories. Developer получает уже не разговор целиком, а небольшую задачу с контекстом, acceptance criteria и ссылками на решения. Verification и code review проверяют не убедительность ответа, а соответствие реализации артефактам.

Официальная карта BMAD группирует жизненный цикл в Analysis, Planning, Solutioning и Implementation, причём документы ранних этапов становятся входом последующих. Команды включают создание PRD, architecture, project context, epics, stories, sprint planning и code review (официальный репозиторий BMAD Method).

Одна Aegis story глазами разных ролей

Возьмём обнаруженный пробел в incident UI. Формулировка “подключить кнопки к API” кажется готовой story, но Analyst сначала уточнил бы пользовательский результат: acknowledge из браузера должен менять тот же incident, создавать тот же timeline event и останавливать ту же escalation, что и acknowledge из чата. PM закрепил бы это в требованиях и отдельно назвал resolve, handoff, ошибки и повторный клик. Так неопределённое “подключить” превращается в наблюдаемое поведение.

Architect проверил бы, что web не создаёт второй вариант state machine: переходы остаются в backend service, UI вызывает контракт и обновляет данные после ответа. На этапе планирования работа превратилась бы в вертикальный slice, а не в две независимые задачи “сделать API” и “сделать кнопки”. Developer получил бы story вместе с API contract, состояниями incident, design system и тестовым планом. Verification должна открыть реальный сценарий, вызвать acknowledge и увидеть изменение в API и timeline. Именно на этом шаге fixtures перестают выглядеть почти готовой интеграцией.

Для Aegis ценность этой схемы особенно видна на архитектурных развилках. Например, можно попросить Developer сразу добавить Redis queue. Он, вероятно, справится. Но Architect сначала спросит, нужна ли отдельная очередь при небольшом объёме, уже обязательном PostgreSQL и требовании простого self-hosted deployment. Решение “job table в PostgreSQL, без Redis” становится не случайной экономией, а зафиксированным invariant.

Общие артефакты работают как внешняя память. Product brief объясняет зачем, PRD говорит что, architecture ограничивает как, story задаёт следующий кусок, а verification возвращает факты о результате. Новый агент не обязан восстанавливать проект по истории чата и названиям функций. Он начинает с подготовленного контекста.

Важно не только наличие файлов, но и направление handoff. Для Aegis требование “частичный сбой chat connector не отменяет успешную доставку” идёт из PRD в connector pattern, затем в story и тесты по каждому provider. Developer может предложить другой механизм, но не может незаметно заменить правило общим 500. Если правило действительно надо поменять, изменение возвращается к владельцу требования, а не маскируется локальным рефакторингом.

Жёсткие gates и мягкая последовательность

BMAD не требует одинаковой церемонии для каждой правки. Discovery и architecture можно сократить для маленькой локализации или очевидного bugfix, а для нового routing policy пройти полностью. Это мягкая часть: глубина workflow зависит от риска и масштаба.

Жёсткая часть начинается там, где Aegis обещает проверяемое качество. Нельзя сократить acceptance criteria, lint, type, test, Definition of Done или реальный verification только потому, что implementation выглядит убедительно. Роль Developer получает больше автономии именно внутри этих границ: самостоятельно исследовать код, предложить план, провести slice и исправить review findings. Автономия здесь означает более широкий контур ответственности, а не право переписать контракт по дороге.

Важна и семантика blocked. Современные workflow BMAD умеют завершаться с машинно читаемым состоянием blocked и причиной, например при неясном intent, незавершённой предыдущей story или проваленной verification. Orchestrator должен воспринимать это как сигнал маршрутизации, а не как повод объявить успех другими словами (BMAD: blocked states и обязанности orchestrator).

Для Aegis blocked мог бы означать простой вопрос: должен ли повторный acknowledge возвращать успех или конфликт? Пока API contract не отвечает, агент фиксирует пробел и эскалирует решение человеку. Если предыдущая story ещё In Review, следующая не притворяется независимой. Если verification не увидела timeline event, работа возвращается в реализацию. Хороший агент умеет не только делать, но и остановиться до того, как догадка станет частью продукта.

Кому на самом деле полезен BMAD

Здесь есть важная управленческая оговорка. BMAD полезен прежде всего разработчику. Он расширяет поверхность ответственности, на которой инженер может работать самостоятельно: от неясного запроса через discovery и architecture до implementation и verification.

Разработчик получает не кнопку “сделать фичу”, а способ последовательно менять режим мышления. Сначала уточнить ценность, потом зафиксировать требования, проверить архитектурные последствия, нарезать работу, реализовать, доказать готовность. AI берёт на себя большую часть черновой работы, поиска противоречий и механики, но решения остаются видимыми.

Руководителю полезен разработчик, который использует BMAD. Сам BMAD не является management dashboard, не заменяет roadmap, delivery ownership или разговор о рисках. Если просто установить framework и ждать, что он начнёт управлять командой, получится новый слой ceremony с очень разговорчивыми участниками.

Практическая выгода для руководителя косвенная, но существенная: инженер приносит лучше сформулированные вопросы, оставляет проверяемые решения, раньше сообщает о блокировке и способен провести задачу через более широкий контур. Review становится обсуждением trade-offs, а не археологией промптов. Это не отменяет специализацию команды. Это уменьшает количество потерь между специализациями.

BMAD здесь важен как объяснение процесса, но Aegis появился не из названия методологии. Продукт появился из конкретной проблемы, ограничений, кода, тестов и обратной связи. Framework полезен ровно до тех пор, пока помогает удерживать эту связь.

Честный статус: open-source beta

Aegis сейчас является open-source beta. Не enterprise-ready платформой, не drop-in replacement для любого Opsgenie и не поводом отключать рабочий paging в пятницу вечером.

Backend-путь уже реален: webhook intake, хранение alert, deduplication, routing rules, incident lifecycle, Jira ticket provider, Slack и eXpress connectors, interactive acknowledge callbacks, escalation и handoff. Worker выполняет process_alert, escalate_incident, materialise_oncall и notify_handoff. Есть OIDC, RBAC, audit, health и readiness probes, Prometheus metrics. Текущий implementation status зафиксирован в README проекта на febfd70.

Работают API-backed страницы alert workspace, integrations, dashboard и setup wizard. Есть поиск, фильтры, saved views, export и основные analytics. Для deployment доступны Docker Compose для локальной сборки и единый GHCR image для запуска API, worker и web с внешним PostgreSQL. Kubernetes manifest пока остаётся sketch, Helm сознательно вынесен за scope.

И есть важный пробел: incidents page в web сейчас использует demo fixtures в App.tsx. Кнопки acknowledge, resolve и handoff меняют React state, но ещё не вызывают существующие backend endpoints. То есть backend flow и UI-компоненты построены и тестируются, однако критичный incident UI не прошёл полный API wiring. Этот разрыв прямо отмечен в спецификации incident management на febfd70.

Это хороший пример того, как “готово по слоям” отличается от “готово как продукт”. Именно такой разрыв one-shot подход любит не замечать: API есть, UI есть, значит feature якобы есть. В beta я предпочитаю назвать щель щелью. До production readiness нужны end-to-end wiring, эксплуатационная проверка под реальной нагрузкой, security review, backup и restore drills, upgrade path и время на неприятные сюрпризы, которые невозможно сгенерировать заранее.

Что я попробовал до BMAD

OpsKnight уже упоминался в начале. Его официальное позиционирование действительно близко: self-hosted incident response, on-call schedules, policy-based routing, notifications и status pages. Мой отказ был не сравнением таблиц “у кого больше галочек”. Проект не совпал с нужным мне интеграционным и эксплуатационным контуром, а мой тестовый опыт не дал оснований строить на нём внутренний критичный процесс. Для другого набора требований вывод может быть противоположным.

Ещё одним этапом был GSD, Get Shit Done. Он рано и очень наглядно показал мне ценность context engineering: хранить vision, requirements, roadmap, state и research как файловые артефакты, а выполнение делить на фазы и небольшие plans. Официальная архитектура GSD прямо называет систему meta-prompting и context engineering framework и описывает .planning как память проекта (GSD Architecture). Эту идею я считаю сильной и сохранил в собственном процессе.

Позже вокруг исходного проекта возникла криптовалютная ассоциация, после которой я перестал ему доверять. Здесь важно аккуратно разделить факты и выводы. Сопровождающий community fork сообщает, что токен $GSD, связанный с проектом, публично связали с rug-pull, что связь с исходным maintainer была потеряна и что community создала продолжение. В том же объявлении прямо сказано: авторы fork не могут подтвердить, было ли это действием maintainer, выходом сооснователя или захватом аккаунта (объявление community-maintained fork).

Поэтому я не утверждаю, кто и что сделал. Подтверждено лишь, что такая публичная ассоциация возникла и что существует community fork; attribution остаётся неподтверждённой. Мой вывод является личной интерпретацией: для инструмента, которому я разрешаю читать репозиторий, менять файлы и запускать команды, цепочка доверия является частью технического качества. Когда я перестал понимать, кому и чему доверяю, я перестал использовать upstream. Полезная идея context engineering от этого не стала менее полезной.

Три практических вывода

Первый: AI ускоряет производство вариантов, а не принятие ответственности. Он быстро предложит schema, endpoint, component и test. Инженер всё равно должен определить, какую проблему решаем, какие ограничения нельзя нарушать и чем доказать готовность.

Второй: документы полезны, когда они управляют работой. Product brief без связи с PRD, stories и gates превращается в литературу. Harness работает, когда каждое решение доходит до acceptance criteria, кода, теста и review, а расхождение возвращает процесс назад.

Третий: самый ценный результат AI-assisted delivery не количество сгенерированных строк. Это короткий и наблюдаемый feedback loop. Чем раньше видно, что UI живёт на fixtures, API contract разошёлся с типами или story слишком велика, тем дешевле исправление.

Что я сделал бы иначе во второй раз

Во-первых, подключил бы BMAD с начала проекта. Самодельный harness сработал, но часть структуры я создавал по мере обнаружения очередной проблемы. Готовые роли и workflow раньше заставили бы отделить discovery от architecture, а verification от ощущения завершённости.

Во-вторых, раньше провёл бы end-to-end wiring критичных UI-сценариев. Я слишком долго позволял backend и frontend развиваться параллельно с fixtures как временным мостом. Временные мосты известны тем, что быстро получают почтовый адрес. Для incident management первый slice должен был сразу пройти от webhook до реальной кнопки acknowledge в браузере.

В-третьих, фиксировал бы фактическое время и стоимость токенов по stories. Коммиты и тесты хорошо показывают структуру работы, но плохо отвечают на экономический вопрос. На следующем проекте я хочу видеть для каждого slice календарное время, активные часы, число repair loops и token cost. Только так можно сравнивать workflow, модели и уровень детализации без впечатлений.

Я начинал с вопроса, чем заменить Opsgenie в конкретном self-hosted окружении. Закончил работающей beta и более общим выводом.

Модель может за минуты создать убедительный фрагмент кода. Но она не удерживает сама по себе продуктовый замысел, архитектурные границы, порядок зависимостей, эксплуатационный риск и честное определение готовности. Всё это создаёт harness: требования, артефакты, роли, короткие циклы, gates и право остановиться.

AI создаёт код. Harness создаёт продукт.