Skip to content

Split View: Issue에서 배포까지 — AI가 리뷰하고 CI/CD가 검증·배포하는 자동화 파이프라인 만들기 (2025)

|

Issue에서 배포까지 — AI가 리뷰하고 CI/CD가 검증·배포하는 자동화 파이프라인 만들기 (2025)

프롤로그 — 선(line)이 아니라 고리(loop)

지난 글에서는 AI 개발 자동화의 부품을 다뤘다 — 어떤 에이전트가 있고, GitHub과 어떻게 연동되고, 티켓을 어떻게 맡기는지.

이번 글은 그 부품들을 하나의 파이프라인으로 조립한다. 목표는 이것이다:

사람이 Issue를 하나 올린다. 자리를 비운다. 잠시 후 검증된 배포가 프로덕션에 올라가 있다. 사람이 개입한 건 단 하나 — 머지 버튼을 누른 결정뿐이다.

그리고 더 중요한 것: 이건 선이 아니라 고리다. 배포가 끝이 아니다. 모니터링이 이상을 감지하면 자동 롤백하고, 그 사건이 새 Issue가 되어 파이프라인의 입구로 다시 들어온다. 시스템이 스스로를 고친다.

이 글은 그 고리를 7개 스테이지로 쪼개고, 각 스테이지의 게이트를 설계하고, GitHub Actions로 전부 구현한다. 핵심 원칙은 하나다:

"작업은 자동화하고, 결정은 게이트한다 (Automate the work, gate the decisions)."

AI가 코드를 짜고 리뷰하고 배포하게 두되, 되돌릴 수 없는 결정(프로덕션 머지, 스키마 변경, 보안 로직)에는 반드시 사람 또는 강한 자동 검증을 세운다.


0장 · 전체 파이프라인 조감도

먼저 큰 그림. 7개 스테이지와 각 스테이지를 누가 소유하는지.

 ┌────────────────────────────────────────────────────────────────┐
 │                                                                │
 │  [S1] Issue ──ai-ready 라벨──▶ AI 에이전트 ──▶ Draft PR        │
 │         ▲                                         │            │
 │         │                                         ▼            │
 │         │                              [S2] AI 코드 리뷰       │
 │         │                                  (생성≠리뷰)         │
 │         │                                         │            │
 │         │                                         ▼            │
 │         │                              [S3] CI 검증 게이트     │
 │         │                          lint·type·test·build·E2E    │
 │         │                                         │            │
 │         │                          ┌── 실패 ──────┤            │
 │         │                          ▼              ▼ 통과       │
 │         │                   AI 자가 수정    [S4] Human Gate    │
 │         │                   (로그 읽고 재푸시)   머지 결정      │
 │         │                                         │            │
 │         │                                         ▼            │
 │         │                              [S5] CD: Preview        │
 │         │                              → Canary → Production    │
 │         │                                         │            │
 │         │                                         ▼            │
 │         │                              [S6] 자동 검증          │
 │         │                          smoke·E2E·SLO 게이트        │
 │         │                                         │            │
 │         │                          ┌── 실패 ──────┤            │
 │         │                          ▼              ▼ 통과       │
 │         └──── 자동 Issue 생성 ◀─ [S7] 모니터링 & 피드백        │
 │              (자동 롤백 + 사건화)     배포 후 관측, 롤백 트리거 │
 │                                                                │
 └────────────────────────────────────────────────────────────────┘
스테이지이름소유출력
S1Issue → PRAI 에이전트Draft PR
S2AI 코드 리뷰AI 리뷰어 (생성과 다른 모델)인라인 코멘트 + APPROVE/REQUEST_CHANGES
S3CI 검증 게이트CI 시스템초록불/빨간불
S4Human Gate사람머지 결정
S5CD 배포CD 시스템Preview → Canary → Production
S6자동 검증검증 시스템승격 또는 롤백
S7모니터링 & 피드백관측 시스템정상 또는 자동 Issue

이 글의 나머지는 각 스테이지를 하나씩 깊게 판다.


1장 · 파이프라인 설계 5원칙

구현 전에 원칙을 못 박는다. 이게 흔들리면 파이프라인이 위험해진다.

원칙 1 — 모든 스테이지는 fail-closed

스테이지가 실패하거나 판단이 불확실하면 멈춘다. 통과시키지 않는다. AI 리뷰가 헷갈리면 → REQUEST_CHANGES. CI가 flaky하면 → 빨간불. 검증이 모호하면 → 롤백. 의심스러우면 진행하지 않는다.

원칙 2 — 모든 스테이지는 되돌릴 수 있어야 한다

PR은 close, 머지는 revert, 배포는 rollback. 각 스테이지에 "취소 버튼"이 없으면 그 스테이지는 자동화하면 안 된다.

원칙 3 — 모든 스테이지는 관측 가능해야 한다

누가·언제·무엇을·왜 했는지 로그가 남는다. AI가 한 일은 특히. 디버깅 불가능한 자동화는 시한폭탄이다.

원칙 4 — 멱등성 (Idempotency)

같은 Issue가 두 번 트리거돼도 PR이 두 개 생기면 안 된다. 같은 커밋이 두 번 배포돼도 안전해야 한다. 재시도가 안전해야 자동화가 안전하다.

원칙 5 — 신뢰 사다리 (Trust Ladder)

처음부터 다 자동화하지 않는다. 저위험·고빈도 작업부터 자동화하고, 성공률 데이터가 쌓이면 자동화 범위를 넓힌다. 문서 수정 자동 머지 → 의존성 범프 자동 머지 → … 한 칸씩.


2장 · S1 — Issue → PR (생성 스테이지)

지난 글에서 다룬 부분이라 핵심만. ai-ready 라벨이 트리거, AI 에이전트가 브랜치를 만들고 구현하고 Draft PR을 연다.

이 스테이지의 진짜 목표는 "코드를 짜는 것"이 아니라 **"리뷰 가능한 PR을 낳는 것"**이다. 뒤의 모든 스테이지가 PR 품질에 의존한다.

PR이 "리뷰 가능하게 태어나는" 조건

  • Draft 상태로 생성 — 사람이 "Ready for review"로 승격하기 전엔 머지 불가.
  • Issue 링크 — PR 본문에 Closes #123. 머지 시 Issue 자동 close.
  • 작업 요약 — 에이전트가 "무엇을·왜" 했는지 본문에 남긴다. 리뷰어의 출발점.
  • 일관된 PR 제목[AI] fix: ... 처럼. 뒤 스테이지가 제목으로 분기한다.
  • ai-generated 라벨 — 추적용.

멱등성 — 중복 PR 방지

# 같은 이슈에 PR이 이미 있으면 새로 안 만든다
- name: Check existing PR
  id: check
  run: |
    EXISTING=$(gh pr list --search "in:title #${{ github.event.issue.number }}" --json number --jq 'length')
    echo "exists=$EXISTING" >> "$GITHUB_OUTPUT"

- name: Run agent
  if: steps.check.outputs.exists == '0'
  uses: anthropics/claude-code-action@v1
  # ...

3장 · S2 — AI 코드 리뷰 자동화 (핵심 스테이지)

여기가 이 파이프라인의 심장이다. 사람 리뷰 전에 AI 리뷰를 한 겹 끼운다.

왜 사람 리뷰 전에 AI 리뷰인가

  • 노이즈 필터 — 명백한 실수(누락된 에러 처리, 타입 불일치, 컨벤션 위반)를 사람이 보기 전에 잡는다.
  • 사람 리뷰 시간 절약 — 사람은 "이게 맞는 접근인가" 같은 판단에 집중. 잔손질은 AI가.
  • 24/7 — PR이 새벽에 올라와도 즉시 리뷰.

철칙: 생성 ≠ 리뷰 (다른 모델, 다른 에이전트)

PR을 만든 에이전트가 자기 PR을 리뷰하게 두지 마라. 자기 코드의 사각지대는 자기가 못 본다. 가능하면 다른 모델로 리뷰한다. Claude가 짰으면 다른 도구가 리뷰하거나, 최소한 별도 세션·별도 프롬프트로.

생성 에이전트 (Claude/Copilot) ──▶ PR
리뷰 에이전트 (다른 모델/도구) ──▶ 리뷰 코멘트
사람 ──────────────────────────▶ 최종 결정

도구 지형도

도구형태특징
CodeRabbitGitHub AppPR마다 자동 리뷰, .coderabbit.yaml 설정, 요약+인라인
GreptileGitHub App코드베이스 전체 컨텍스트 기반 리뷰
GitHub Copilot 리뷰내장Copilot을 PR 리뷰어로 지정 가능
Claude Code ActionActions@claude 또는 워크플로로 리뷰, 커스터마이즈 강함

AI 리뷰가 잘 잡는 것 / 못 잡는 것

잘 잡음못 잡음
누락된 null·에러 처리"이게 올바른 아키텍처인가"
컨벤션·네이밍 위반비즈니스 요구사항 충족 여부
명백한 보안 패턴 (SQLi, 하드코딩 시크릿)도메인 특화 미묘한 버그
테스트 커버리지 공백성능의 미묘한 트레이드오프
흔한 버그 패턴팀의 암묵적 맥락

그래서 사람 게이트(S4)가 필요하다. AI 리뷰는 사람을 대체하지 않고 사람의 부담을 줄인다.

구현 — Claude Code Action으로 리뷰

name: AI Review
on:
  pull_request:
    types: [opened, synchronize, ready_for_review]

jobs:
  ai-review:
    # draft가 아닐 때만 — 작업 중인 PR은 리뷰 안 함
    if: github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: |
            이 PR을 리뷰하라. 다음에 집중:
            - 정확성: 로직 버그, 엣지 케이스, 누락된 에러 처리
            - 보안: 입력 검증, 시크릿, 권한
            - 테스트: 변경에 대한 커버리지가 충분한가
            - 컨벤션: CLAUDE.md를 따르는가

            문제는 해당 라인에 인라인 코멘트로 남겨라.
            마지막에 요약과 함께 APPROVE 또는 REQUEST_CHANGES 를 명시하라.
            사소한 스타일은 nit: 으로 표시해 사람이 무시할 수 있게 하라.

AI 리뷰를 "필수 체크"로 만들기

리뷰가 코멘트만 남기면 무시당한다. 상태 체크(status check)로 만들어 브랜치 보호 규칙에 넣으면, REQUEST_CHANGES일 때 머지 자체가 막힌다. 리뷰 액션이 GitHub Checks API로 pass/fail을 보고하도록 구성한다.


4장 · S3 — CI 검증 게이트

AI가 짠 코드를 기계가 검증하는 단계. CI가 약하면 이 파이프라인 전체가 약하다.

계층화된 체크

빠름 ──────────────────────────────────▶ 느림
lint → typecheck → unit test → build → integration → E2E
  │        │           │          │         │          │
 초 단위  초 단위    초~분       분        분         분~십분

빠른 체크를 앞에 둬서 빨리 실패하게 한다. lint에서 깨질 걸 E2E까지 가서 알 필요 없다.

CI를 "AI가 읽을 수 있게" 만들기

이게 핵심인데 자주 놓친다. CI 실패 메시지가 친절하면 AI가 스스로 고친다.

  • Error: test failed (나쁨) → Error: expected status 200, got 401 at auth.test.ts:42 (좋음)
  • 실패한 명령, 파일·라인, 기대값·실제값을 출력하라.
  • AI 에이전트는 이 로그를 읽고 다음 커밋에서 자가 수정한다.

자가 수정 루프

PR 푸시 ──▶ CI 실행 ──▶ 실패
            에이전트가 CI 로그를 읽음
            원인 파악 → 수정 커밋 푸시
                     CI 재실행 ──▶ 통과

이 루프에 상한을 둔다 — 3회 시도 후에도 빨간불이면 사람을 호출한다. 무한 자가 수정은 비용 폭탄이다.

# CI 실패 시 에이전트에 로그를 주고 수정 시도 — 단, 라벨로 횟수 제한
- name: Self-heal on CI failure
  if: failure() && !contains(github.event.pull_request.labels.*.name, 'ai-heal-exhausted')
  uses: anthropics/claude-code-action@v1
  with:
    anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
    prompt: |
      CI가 실패했다. 아래 로그를 읽고 원인을 고쳐 커밋하라.
      추측하지 말고 로그가 가리키는 것만 고쳐라.

브랜치 보호 — 게이트를 강제

Settings → Branches → main 보호 규칙:
  ✅ Require status checks: ci/lint, ci/test, ci/build, ai-review
  ✅ Require branches to be up to date before merging
  ✅ Require a pull request before merging
  ✅ Require approvals: 1+ (S4 사람 게이트)
  ✅ Dismiss stale approvals when new commits pushed
  ✅ Do not allow bypassing the above

5장 · S4 — Human Gate (사람이 지키는 관문)

자동화의 핵심은 역설적으로 "사람이 어디를 지키는가"를 명확히 하는 것이다.

사람이 반드시 결정해야 하는 것

  • 머지 결정 그 자체 — "이걸 main에 넣는다"는 책임은 사람이 진다.
  • 아키텍처 방향 — "이 접근이 맞는가"는 AI가 판단 못 한다.
  • 비즈니스 요구사항 충족 — Issue의 의도를 진짜 만족했는가.
  • 보안·데이터 민감 변경 — 실수 비용이 큰 영역.

사람 리뷰를 빠르게 만드는 것들

S4가 병목이 되지 않으려면, 사람이 PR을 받았을 때 이미:

  • ✅ AI 리뷰가 끝나 있다 (잔손질 다 처리됨)
  • ✅ CI가 초록불이다 (기계 검증 통과)
  • ✅ PR이 작다 (리뷰 가능한 크기)
  • ✅ 설명이 좋다 (무엇을·왜)
  • ✅ Preview 배포 URL이 붙어 있다 (실제로 눌러볼 수 있음)

그러면 사람 리뷰는 "판단"만 하면 된다. 5분이면 끝난다.

자동 머지 — 언제 OK인가

GitHub의 auto-merge는 "모든 필수 체크 통과 + 필수 승인 완료 시 자동 머지"다. 위험하지만, 특정 변경 유형에는 안전하다.

자동 머지 OK자동 머지 금지
문서·주석 수정애플리케이션 로직
의존성 패치 범프 (CI 통과 시)스키마 마이그레이션
린트·포맷 자동 수정보안·인증 로직
생성된 타입·SDK 갱신인프라·IaC
# 의존성 PR이고 CI 통과 + AI 리뷰 APPROVE면 자동 머지 활성화
- name: Enable auto-merge for safe deps
  if: |
    contains(github.event.pull_request.labels.*.name, 'dependencies') &&
    github.event.pull_request.user.login == 'dependabot[bot]'
  run: gh pr merge --auto --squash "${{ github.event.pull_request.number }}"

에스컬레이션 경로

AI(생성이든 리뷰든)가 불확실하면 사람을 태그한다. "이 변경은 인증 로직을 건드립니다 — 보안 담당자 리뷰 필요" 같은 코멘트를 자동으로 남기고 needs-human 라벨을 붙인다.


6장 · S5 — CD: Preview → Canary → Production

머지됐다. 이제 배포. 핵심은 한 번에 다 배포하지 않는 것이다.

Preview 배포 — PR마다

PR이 열리면 격리된 실제 환경에 배포해 고유 URL을 준다. Vercel·Netlify·Cloudflare는 기본 제공, 자체 인프라는 PR별 네임스페이스로 구현.

→ S4의 사람 리뷰어가 코드가 아니라 동작하는 결과물을 본다. AI 리뷰어도 Preview URL에 E2E를 돌릴 수 있다.

머지 후 — 단계적 전달 (Progressive Delivery)

main 머지
Canary 배포 (트래픽 5%)
   ▼  [S6 자동 검증]
smoke test + SLO 관측 (5~15분)
   ├── 실패 ──▶ 자동 롤백 (Canary 제거)
   ▼ 통과
Production 승격 (트래픽 100%)
  • Canary — 새 버전에 트래픽 일부(5~10%)만. 문제가 나도 영향 범위가 작다.
  • Feature Flag — 코드는 배포하되 기능은 꺼둔 채. 켜는 건 별도 결정.
  • 환경 승격 — dev → staging → prod. 각 단계가 게이트.

GitHub Environments로 게이트 구현

name: Deploy
on:
  pull_request:
    types: [closed]
    branches: [main]

jobs:
  deploy-canary:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    environment: canary          # 보호 규칙: 없음 (자동)
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy.sh canary

  promote-production:
    needs: [deploy-canary, verify-canary]
    runs-on: ubuntu-latest
    environment: production      # 보호 규칙: required reviewers = 사람 게이트
    steps:
      - run: ./scripts/deploy.sh production

environment: productionrequired reviewers를 걸면, 프로덕션 승격에 사람 승인이 강제된다 — 워크플로 안에 박힌 또 하나의 Human Gate.


7장 · S6 — 자동 검증 (Auto-Verification)

배포했다고 끝이 아니다. 배포된 것이 실제로 동작하는지 기계가 확인한다.

배포 후 검증 계층

검증무엇을언제
Smoke test핵심 경로 몇 개 (로그인, 헬스체크)배포 직후 즉시
E2E주요 사용자 시나리오Canary 단계
Synthetic monitoring외부에서 주기적 핑 (Checkly, Datadog)지속
SLO 관측에러율·레이턴시·가용성Canary 윈도우 동안
Visual regressionUI 픽셀 차이Preview/Canary

검증 게이트 — Canary를 승격할까 롤백할까

  verify-canary:
    needs: deploy-canary
    runs-on: ubuntu-latest
    outputs:
      verdict: ${{ steps.gate.outputs.verdict }}
    steps:
      - name: Smoke test
        run: ./scripts/smoke-test.sh https://canary.example.com

      - name: Observe SLO for 10 minutes
        id: gate
        run: |
          sleep 600
          ERROR_RATE=$(curl -s "$METRICS_API/error-rate?env=canary&window=10m")
          P99=$(curl -s "$METRICS_API/latency-p99?env=canary&window=10m")
          # 에러율 1% 초과 또는 p99 500ms 초과면 fail
          if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then
            echo "verdict=rollback" >> "$GITHUB_OUTPUT"; exit 1
          fi
          echo "verdict=promote" >> "$GITHUB_OUTPUT"

  rollback-canary:
    needs: verify-canary
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - run: ./scripts/rollback.sh canary

검증이 실패하면 → Canary 자동 제거. 프로덕션 트래픽은 애초에 5%만 영향받았고, 그것도 즉시 회수된다.


8장 · S7 — 모니터링 & 피드백 루프 (고리를 닫는다)

여기서 파이프라인이 선에서 고리가 된다. 배포 후에도 시스템은 계속 지켜본다.

자동 롤백 트리거

프로덕션 승격 후에도 관측은 계속된다. SLO를 깨면 자동 롤백.

  • 에러 예산 소진 (error budget burn rate) — 빠르게 타들어가면 롤백.
  • 레이턴시 급등 — p99가 임계 초과.
  • 핵심 비즈니스 메트릭 하락 — 결제 성공률, 가입 전환율 등.

자가 치유 루프 — 사건이 다시 Issue가 된다

이게 고리의 핵심이다. 자동 롤백이 일어나면, 그 사건을 새 Issue로 만들어 파이프라인 입구(S1)로 돌려보낸다.

name: Monitor & Auto-Rollback
on:
  schedule:
    - cron: '*/5 * * * *'      # 5분마다 SLO 점검
  workflow_run:
    workflows: ["Deploy"]
    types: [completed]

jobs:
  slo-check:
    runs-on: ubuntu-latest
    steps:
      - name: Check production SLO
        id: slo
        run: |
          BURN=$(curl -s "$METRICS_API/error-budget-burn?env=prod&window=1h")
          echo "burn=$BURN" >> "$GITHUB_OUTPUT"

      - name: Rollback + file issue if breaching
        if: ${{ steps.slo.outputs.burn > 2.0 }}
        run: |
          ./scripts/rollback.sh production
          gh issue create \
            --title "Auto-rollback: error budget burn rate ${{ steps.slo.outputs.burn }}" \
            --label "ai-ready,incident,priority-high" \
            --body "프로덕션 SLO 위반으로 자동 롤백했다.
            마지막 배포: ${{ github.sha }}
            burn rate: ${{ steps.slo.outputs.burn }}
            에이전트는 롤백된 커밋의 diff를 분석하고 원인을 진단해
            수정 PR을 제출하라."

이 Issue에 ai-ready 라벨이 붙어 있으므로 → S1이 다시 트리거된다. AI가 롤백된 커밋을 분석하고, 수정 PR을 만들고, AI 리뷰 → CI → 사람 게이트 → 재배포. 고리가 닫혔다.

닫힌 고리의 의미

   Issue ──▶ PR ──▶ 리뷰 ──▶ CI ──▶ 머지 ──▶ 배포 ──▶ 검증 ──▶ 모니터
     ▲                                                            │
     └──────────────── 사건이 새 Issue로 ◀──────────────────────────┘

시스템이 자기가 낸 문제를 스스로 입력으로 받아 고친다. 사람은 여전히 머지 게이트를 지키지만, 탐지·진단·수정 착수·재배포의 반복 노동에서 해방된다.


9장 · 전체 구현 — GitHub Actions로 엮기

지금까지의 스테이지를 실제 파일로. 5개 워크플로가 이벤트로 연결된다.

워크플로 지도

파일트리거역할
ai-resolve.ymlissues: labeledS1: Issue → PR
ai-review.ymlpull_request: opened/synchronizeS2: AI 리뷰
ci.ymlpull_request: opened/synchronizeS3: 검증 게이트
deploy.ymlpull_request: closed (merged)S5: Canary → Production
monitor.ymlschedule + workflow_runS6/S7: 검증·롤백·피드백

이벤트로 어떻게 연결되나

GitHub Actions는 중앙 오케스트레이터가 없다. 이벤트가 곧 연결선이다.

issue.labeled('ai-ready')  ──▶ ai-resolve.yml ──▶ (PR 생성)
pull_request.opened ◀──────────────────────────────────┘
   ├──▶ ai-review.yml   (AI 리뷰 → 체크 보고)
   └──▶ ci.yml          (검증 → 체크 보고)
   [브랜치 보호: 모든 체크 + 사람 승인 대기]            │
pull_request.closed(merged) ──▶ deploy.yml ──▶ (Canary → 검증 → Production)
workflow_run('Deploy' completed) ──▶ monitor.yml       │
schedule('*/5')                  ──▶ monitor.yml ◀─────┘
   └──▶ SLO 위반 시: rollback + issue.create('ai-ready')
                                          └──▶ (다시 ai-resolve.yml — 고리 완성)

ai-resolve.yml — S1

name: AI Resolve
on:
  issues:
    types: [labeled]

jobs:
  resolve:
    if: github.event.label.name == 'ai-ready'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
      issues: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Skip if PR already exists
        id: dedup
        run: |
          N=$(gh pr list --search "#${{ github.event.issue.number }} in:body" --json number --jq 'length')
          echo "exists=$N" >> "$GITHUB_OUTPUT"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Run agent
        if: steps.dedup.outputs.exists == '0'
        uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: |
            Issue #${{ github.event.issue.number }}: ${{ github.event.issue.title }}

            ${{ github.event.issue.body }}

            이 이슈를 구현하라. 브랜치를 만들고, 변경하고, 테스트를 통과시키고,
            "Closes #${{ github.event.issue.number }}" 를 포함한 Draft PR을 열어라.
            CLAUDE.md 컨벤션을 따르고, PR 본문에 무엇을·왜 했는지 요약하라.

ci.yml — S3

name: CI
on:
  pull_request:
    types: [opened, synchronize, ready_for_review]

concurrency:
  group: ci-${{ github.event.pull_request.number }}
  cancel-in-progress: true     # 새 푸시 시 이전 실행 취소 — 비용 절감

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint        # 빠른 것 먼저
      - run: pnpm typecheck
      - run: pnpm test
      - run: pnpm build

deploy.yml — S5 (6장 참조, 핵심만)

name: Deploy
on:
  pull_request:
    types: [closed]
    branches: [main]

jobs:
  canary:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    environment: canary
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy.sh canary

  verify:
    needs: canary
    runs-on: ubuntu-latest
    steps:
      - run: ./scripts/smoke-test.sh https://canary.example.com
      - run: ./scripts/observe-slo.sh canary 600   # 10분 관측

  production:
    needs: verify
    runs-on: ubuntu-latest
    environment: production       # required reviewers = Human Gate
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy.sh production

  rollback:
    needs: [canary, verify]
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - run: ./scripts/rollback.sh canary

리포 설정 체크리스트

  • Secrets: ANTHROPIC_API_KEY, 배포 자격증명.
  • Environments: canary(자동), production(required reviewers 지정).
  • Branch protection (main): 필수 체크에 ci/verify, ai-review 포함. 필수 승인 1+.
  • Labels: ai-ready, ai-generated, needs-human, incident, dependencies.

10장 · 결정 게이트 매트릭스 — 무엇을 자동화하고 무엇을 사람이

파이프라인의 진짜 설계는 "어디까지 자동화하는가"다. 변경 유형별로 게이트를 다르게 건다.

변경 유형AI 생성AI 리뷰CI사람 게이트자동 머지배포
문서·주석선택✅ 가능자동
의존성 패치 범프✅ 필수선택✅ 조건부Canary 자동
버그 픽스 (좁은 범위)✅ 1명Canary→자동 승격
기능 추가✅ 1명+Canary→사람 승격
리팩터링✅ + 커버리지✅ 1명Canary→자동 승격
DB 마이그레이션⚠️ AI 보조✅ 필수사람 트리거
보안·인증 로직⚠️ AI 보조✅ 2명사람 트리거
인프라·IaC⚠️ AI 보조✅ plan diff✅ 필수사람 트리거

범례: ✅ 자동 / ⚠️ AI는 보조만, 사람이 주도 / ❌ 안 함

신뢰 사다리를 오르는 법

처음엔 모든 칸을 보수적으로 시작한다 — 자동 머지 다 끄고, 배포는 다 사람 트리거. 그리고 데이터로 한 칸씩 올린다:

  1. 문서 PR 자동 머지를 한 달 켜본다 → 사고 0건 → 유지.
  2. 의존성 범프 자동 머지를 켠다 → CI 통과율·롤백률 관찰 → 안정적이면 유지.
  3. 버그 픽스의 Canary 자동 승격을 켠다 → escape rate(놓친 버그) 관찰.

성공률 데이터 없이 자동화 범위를 넓히지 마라. 신뢰는 적립되는 것이지 선언하는 게 아니다.


11장 · 안전장치 & 실패 모드

자동화가 빠를수록 사고도 빠르다. 각 실패 모드에 방어를 건다.

"AI가 AI를 승인" 문제

가장 위험한 안티패턴. 생성 에이전트가 자기 PR을 리뷰·승인하면 검증이 0이다. 방어:

  • 생성과 리뷰는 다른 도구/모델.
  • AI 리뷰의 APPROVE는 사람 승인을 대체하지 못한다 — 브랜치 보호의 "필수 승인"은 반드시 사람 계정.
  • 봇 계정의 승인은 필수 승인 수에서 제외 설정.

파이프라인을 통한 Prompt Injection

Issue 본문, PR 코멘트, 코드, CI 로그 — 전부 에이전트의 입력이다. 공격자가 거기 명령을 심을 수 있다.

  • 외부 사용자의 Issue/PR은 자동 트리거 금지 — 멤버가 단 ai-ready 라벨만 트리거.
  • 에이전트 토큰 최소 권한 — 프로덕션·시크릿 접근 불가.
  • 워크플로 파일 변경 감지 — PR이 .github/workflows/를 건드리면 무조건 사람 필수 리뷰.

비용 폭주

  • 스텝 상한, 자가 수정 횟수 상한(3회).
  • concurrency + cancel-in-progress로 중복 실행 제거.
  • 동시 실행 에이전트 수 제한.
  • 일/주 단위 비용 알람.

모든 스테이지는 fail-closed + 롤백

스테이지실패 시롤백 수단
S1 생성PR 안 만듦, 이슈에 코멘트
S2 리뷰REQUEST_CHANGES, 머지 차단
S3 CI빨간불, 머지 차단
S4 사람머지 안 함
S5 배포Canary 단계에서 중단rollback.sh canary
S6 검증승격 안 함Canary 제거
S7 모니터자동 롤백 + 이슈화rollback.sh production

감사 — 누가·언제·무엇을

모든 스테이지의 행위가 로그로 남아야 한다. AI가 한 일은 커밋 트레일러(Co-Authored-By:), PR 라벨(ai-generated), 워크플로 실행 기록으로 추적 가능해야 한다. "왜 이게 배포됐지"에 답할 수 없으면 그 자동화는 끄는 게 낫다.


12장 · 운영 — 메트릭과 점진적 신뢰

파이프라인을 켰다고 끝이 아니다. 측정하고 튜닝한다.

추적할 메트릭

메트릭의미건강한 방향
Lead time (Issue→배포)한 바퀴 도는 시간
% auto-merged사람 없이 머지된 비율↑ (단, escape rate 보면서)
AI 리뷰 정밀도AI 리뷰 지적 중 실제 유효 비율
Escape rate파이프라인을 통과한 버그 비율↓ (가장 중요)
Rollback rate배포 중 롤백된 비율낮고 안정적
자가 수정 성공률CI 실패 후 AI가 고친 비율
사람 리뷰 대기 시간S4 큐 대기

DORA 메트릭을 이 파이프라인에

전통적 DORA 4지표가 그대로 적용된다 — Deployment Frequency, Lead Time, Change Failure Rate, MTTR. AI 파이프라인의 목표는 Throughput을 올리되 Stability를 깨지 않는 것이다. Change Failure Rate가 오르면 자동화 범위를 줄여라.

병목은 거의 항상 S4

에이전트 10개가 PR 10개를 5분에 만들어도, 사람 리뷰가 하루 5개면 처리량은 하루 5개다. 생성 속도가 아니라 리뷰 용량이 진짜 상한이다. 그래서 튜닝의 초점은:

  • AI 리뷰(S2)를 강화해 사람 리뷰 부담을 줄인다.
  • PR을 더 작게 쪼개 리뷰를 빠르게 한다.
  • 저위험 변경의 자동 머지를 (데이터를 보며) 늘려 S4를 우회시킨다.

점진적 신뢰 — 데이터로 사다리를 오른다

매주 메트릭을 본다. Escape rate가 낮고 안정적이면 → 신뢰 사다리(10장)를 한 칸 올린다. Escape rate가 오르거나 Rollback rate가 튀면 → 한 칸 내린다. 자동화 범위는 고정값이 아니라 데이터로 조절하는 다이얼이다.


에필로그 — 파이프라인이 곧 팀의 형태

이 글의 파이프라인을 다 구현하면 팀의 일하는 방식이 바뀐다.

  • 개발자는 코드를 적게 타이핑하고 Issue를 잘 쓰고 PR을 잘 리뷰한다.
  • 잔손질 리뷰는 AI가, 판단 리뷰는 사람이.
  • 탐지·진단·수정 착수·재배포의 반복 노동은 시스템이.
  • 사람은 되돌릴 수 없는 결정의 게이트를 지킨다.

핵심 통찰 세 가지로 정리한다.

  1. 선이 아니라 고리다. 배포가 끝이 아니다. 모니터링이 사건을 잡아 다시 Issue로 만들면, 시스템이 자기 문제를 입력으로 받아 고친다.

  2. "작업은 자동화, 결정은 게이트." AI가 코드를 짜고 리뷰하고 배포하게 두되, 머지·스키마·보안 같은 비가역 결정에는 사람 또는 강한 자동 검증을 세운다. fail-closed가 기본값이다.

  3. 신뢰는 적립된다. 처음부터 다 자동화하지 말고, 저위험 작업부터, 메트릭(특히 escape rate)을 보며 한 칸씩 올린다. 자동화 범위는 데이터로 조절하는 다이얼이다.

역설적이게도 이 모든 자동화의 최종 목적은 사람을 일에서 빼는 것이 아니라, 사람을 가장 중요한 곳 — 판단과 결정 — 에 집중시키는 것이다. 파이프라인이 반복 노동을 흡수하면, 팀의 사고는 "어떻게 만들까"에서 "무엇을·왜 만들까"로 올라간다.

14개 항목 체크리스트

  1. 7개 스테이지를 다 식별하고 각 소유자를 정했는가?
  2. 모든 스테이지가 fail-closed인가?
  3. 모든 스테이지에 롤백/취소 수단이 있는가?
  4. 생성 에이전트와 리뷰 에이전트가 분리됐는가 (다른 모델)?
  5. AI 리뷰가 필수 상태 체크로 걸려 있는가?
  6. CI 실패 메시지가 AI가 읽을 만큼 친절한가?
  7. 자가 수정 루프에 횟수 상한이 있는가?
  8. 브랜치 보호에 필수 체크 + 사람 승인이 강제되는가?
  9. 사람 승인은 봇이 아닌 사람 계정만 카운트되는가?
  10. Preview·Canary·Production이 단계적으로 분리됐는가?
  11. production 환경에 required reviewers가 걸려 있는가?
  12. 자동 검증(smoke·SLO)이 Canary 승격을 게이트하는가?
  13. SLO 위반 시 자동 롤백 + 자동 Issue 생성이 되는가 (고리)?
  14. Escape rate를 추적하고 그 데이터로 자동화 범위를 조절하는가?

안티패턴 10가지

  1. 생성 에이전트가 자기 PR을 리뷰·승인.
  2. AI 리뷰 APPROVE를 사람 승인으로 카운트.
  3. 애플리케이션 로직을 자동 머지.
  4. 머지 즉시 100% 프로덕션 배포 (Canary 없음).
  5. 배포 후 검증 없음 (smoke·SLO 게이트 부재).
  6. 자동 롤백은 있는데 사건이 Issue로 안 돌아옴 (고리가 안 닫힘).
  7. CI 실패 메시지가 불친절해 AI 자가 수정 불가.
  8. 자가 수정 루프에 상한 없음 → 비용 폭탄.
  9. 외부 사용자 Issue가 파이프라인을 자동 트리거.
  10. Escape rate 측정 없이 자동화 범위만 넓힘.

다음 글 예고

다음 글 후보: AI 코드 리뷰어 직접 만들기 — 팀 컨벤션을 학습하는 커스텀 리뷰 봇, Progressive Delivery 심층 — Argo Rollouts·Flagger로 SLO 기반 자동 승격, 에이전트 오케스트레이션 — LangGraph로 멀티 스테이지 파이프라인을 상태 머신으로.

"최고의 파이프라인은 사람을 대체하지 않는다. 사람이 결정만 하도록, 나머지 모든 반복을 흡수한다."

— Issue에서 배포까지, 자동화 파이프라인 만들기, 끝.

From Issue to Deploy — Building an Automation Pipeline Where AI Reviews and CI/CD Verifies and Ships (2025)

Prologue — a loop, not a line

In the previous post we covered the parts of AI development automation — which agents exist, how they integrate with GitHub, and how you hand them tickets.

This post assembles those parts into a single pipeline. The goal is this:

A human files one Issue. They step away. A short while later, a verified deployment is live in production. The only thing the human did was press the merge button — the decision.

And more importantly: this is a loop, not a line. Deployment is not the end. When monitoring detects an anomaly, it rolls back automatically, and that incident becomes a new Issue that re-enters the pipeline at its entrance. The system fixes itself.

This post splits that loop into 7 stages, designs the gate for each stage, and implements all of it with GitHub Actions. The core principle is a single one:

"Automate the work, gate the decisions."

Let AI write, review, and deploy code — but for irreversible decisions (production merge, schema changes, security logic), always put a human or a strong automated check in the way.


Chapter 0 · Bird's-eye view of the whole pipeline

The big picture first. The 7 stages and who owns each one.

 ┌────────────────────────────────────────────────────────────────┐
 │                                                                │
 │  [S1] Issue ──ai-ready label──▶ AI agent ──▶ Draft PR          │
 │         ▲                                         │            │
 │         │                                         ▼            │
 │         │                              [S2] AI code review     │
 │         │                                (generate≠review)     │
 │         │                                         │            │
 │         │                                         ▼            │
 │         │                              [S3] CI verification    │
 │         │                          lint·type·test·build·E2E    │
 │         │                                         │            │
 │         │                          ┌── fail ──────┤            │
 │         │                          ▼              ▼ pass       │
 │         │                   AI self-heal   [S4] Human Gate     │
 │         │                  (read logs, repush)  merge decision │
 │         │                                         │            │
 │         │                                         ▼            │
 │         │                              [S5] CD: Preview        │
 │         │                              → Canary → Production    │
 │         │                                         │            │
 │         │                                         ▼            │
 │         │                              [S6] Auto-verification  │
 │         │                          smoke·E2E·SLO gate          │
 │         │                                         │            │
 │         │                          ┌── fail ──────┤            │
 │         │                          ▼              ▼ pass       │
 │         └──── auto Issue creation ◀ [S7] Monitoring & feedback  │
 │             (auto rollback + incident) post-deploy observe,    │
 │                                        rollback trigger        │
 │                                                                │
 └────────────────────────────────────────────────────────────────┘
StageNameOwnerOutput
S1Issue → PRAI agentDraft PR
S2AI code reviewAI reviewer (model different from generator)Inline comments + APPROVE/REQUEST_CHANGES
S3CI verification gateCI systemGreen light / red light
S4Human GateHumanMerge decision
S5CD deploymentCD systemPreview → Canary → Production
S6Auto-verificationVerification systemPromote or roll back
S7Monitoring & feedbackObservability systemHealthy or auto Issue

The rest of this post digs deep into each stage, one at a time.


Chapter 1 · The 5 principles of pipeline design

Nail down the principles before implementing. If these wobble, the pipeline becomes dangerous.

Principle 1 — Every stage is fail-closed

When a stage fails or its judgment is uncertain, it stops. It does not let things through. AI review is confused → REQUEST_CHANGES. CI is flaky → red light. Verification is ambiguous → roll back. When in doubt, do not proceed.

Principle 2 — Every stage must be reversible

A PR can be closed, a merge can be reverted, a deployment can be rolled back. If a stage has no "cancel button," that stage must not be automated.

Principle 3 — Every stage must be observable

Logs record who did what, when, and why. Especially what AI did. Automation you cannot debug is a time bomb.

Principle 4 — Idempotency

If the same Issue is triggered twice, you must not get two PRs. If the same commit is deployed twice, it must be safe. Retries must be safe for automation to be safe.

Principle 5 — The trust ladder

Do not automate everything from the start. Automate low-risk, high-frequency work first, and as success-rate data accumulates, widen the automation scope. Auto-merge for doc fixes → auto-merge for dependency bumps → … one rung at a time.


Chapter 2 · S1 — Issue → PR (the generation stage)

This was covered in the previous post, so just the essentials. The ai-ready label is the trigger; the AI agent creates a branch, implements, and opens a Draft PR.

The real goal of this stage is not "writing code" but "producing a reviewable PR." Every stage downstream depends on PR quality.

The conditions for a PR to be "born reviewable"

  • Created as a Draft — not mergeable until a human promotes it to "Ready for review."
  • Issue linkCloses #123 in the PR body. The Issue closes automatically on merge.
  • Work summary — the agent records "what and why" in the body. The reviewer's starting point.
  • Consistent PR title — like [AI] fix: .... Downstream stages branch on the title.
  • ai-generated label — for tracking.

Idempotency — preventing duplicate PRs

# If a PR already exists for the same issue, don't create a new one
- name: Check existing PR
  id: check
  run: |
    EXISTING=$(gh pr list --search "in:title #${{ github.event.issue.number }}" --json number --jq 'length')
    echo "exists=$EXISTING" >> "$GITHUB_OUTPUT"

- name: Run agent
  if: steps.check.outputs.exists == '0'
  uses: anthropics/claude-code-action@v1
  # ...

Chapter 3 · S2 — AI code review automation (the core stage)

This is the heart of this pipeline. Insert a layer of AI review before human review.

Why AI review before human review

  • Noise filter — catch obvious mistakes (missing error handling, type mismatches, convention violations) before a human sees them.
  • Saves human review time — humans focus on judgment like "is this the right approach." AI handles the touch-ups.
  • 24/7 — even if a PR lands at 3 a.m., it gets reviewed immediately.

Iron rule: generate ≠ review (different model, different agent)

Do not let the agent that created the PR review its own PR. It cannot see the blind spots in its own code. Where possible, review with a different model. If Claude wrote it, have a different tool review it, or at minimum a separate session with a separate prompt.

Generation agent (Claude/Copilot) ──▶ PR
Review agent (different model/tool) ──▶ review comments
Human ─────────────────────────────▶ final decision

Tool landscape

ToolFormCharacteristics
CodeRabbitGitHub AppAuto-reviews every PR, configured via .coderabbit.yaml, summary + inline
GreptileGitHub AppReview based on full-codebase context
GitHub Copilot reviewBuilt-inCopilot can be designated as a PR reviewer
Claude Code ActionActionsReview via @claude or a workflow, strongly customizable

What AI review catches well / poorly

Catches wellCatches poorly
Missing null / error handling"Is this the right architecture"
Convention / naming violationsWhether business requirements are met
Obvious security patterns (SQLi, hardcoded secrets)Subtle domain-specific bugs
Test coverage gapsSubtle performance trade-offs
Common bug patternsThe team's implicit context

This is exactly why the human gate (S4) is needed. AI review does not replace humans — it reduces the human's burden.

Implementation — review with Claude Code Action

name: AI Review
on:
  pull_request:
    types: [opened, synchronize, ready_for_review]

jobs:
  ai-review:
    # only when not a draft — don't review work-in-progress PRs
    if: github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: |
            Review this PR. Focus on:
            - Correctness: logic bugs, edge cases, missing error handling
            - Security: input validation, secrets, permissions
            - Tests: is coverage sufficient for the change
            - Conventions: does it follow CLAUDE.md

            Leave issues as inline comments on the relevant lines.
            At the end, state APPROVE or REQUEST_CHANGES with a summary.
            Mark trivial style points with nit: so a human can ignore them.

Making AI review a "required check"

If review only leaves comments, it gets ignored. Make it a status check and put it in the branch protection rules — then a merge is blocked outright when it is REQUEST_CHANGES. Configure the review action to report pass/fail through the GitHub Checks API.


Chapter 4 · S3 — The CI verification gate

The stage where a machine verifies the code AI wrote. If CI is weak, this whole pipeline is weak.

Layered checks

fast ──────────────────────────────────▶ slow
lint → typecheck → unit test → build → integration → E2E
  │        │           │          │         │          │
seconds  seconds   sec~min      min       min       min~tens of min

Put the fast checks up front so it fails fast. There's no need to go all the way to E2E to learn something will break at lint.

Making CI "readable by AI"

This is the key part, and it's often missed. If CI failure messages are friendly, AI fixes them itself.

  • Error: test failed (bad) → Error: expected status 200, got 401 at auth.test.ts:42 (good)
  • Print the failed command, the file and line, the expected and actual values.
  • The AI agent reads this log and self-heals in the next commit.

The self-heal loop

PR push ──▶ CI runs ──▶ fail
            agent reads the CI log
            identify cause → push a fix commit
                     CI re-runs ──▶ pass

Put a ceiling on this loop — if it's still red after 3 attempts, call a human. Infinite self-healing is a cost bomb.

# On CI failure, give the agent the logs and let it attempt a fix —
# but cap the number of attempts with a label
- name: Self-heal on CI failure
  if: failure() && !contains(github.event.pull_request.labels.*.name, 'ai-heal-exhausted')
  uses: anthropics/claude-code-action@v1
  with:
    anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
    prompt: |
      CI failed. Read the logs below, fix the cause, and commit.
      Don't guess — only fix what the logs point to.

Branch protection — enforce the gate

Settings → Branches → main protection rules:
  ✅ Require status checks: ci/lint, ci/test, ci/build, ai-review
  ✅ Require branches to be up to date before merging
  ✅ Require a pull request before merging
  ✅ Require approvals: 1+ (S4 human gate)
  ✅ Dismiss stale approvals when new commits pushed
  ✅ Do not allow bypassing the above

Chapter 5 · S4 — The Human Gate (the gate a human guards)

The core of automation is, paradoxically, clearly defining "where the human stands guard."

What the human must decide

  • The merge decision itself — the responsibility for "this goes into main" is the human's.
  • Architecture direction — "is this approach right" is something AI cannot judge.
  • Whether business requirements are met — did this genuinely satisfy the Issue's intent.
  • Security and data-sensitive changes — areas where the cost of a mistake is high.

Things that make human review fast

For S4 not to become a bottleneck, when a human receives a PR, it should already have:

  • ✅ AI review done (all touch-ups handled)
  • ✅ CI green (machine verification passed)
  • ✅ The PR is small (a reviewable size)
  • ✅ A good description (what and why)
  • ✅ A Preview deployment URL attached (you can actually click through it)

Then human review only needs to do "judgment." It's done in 5 minutes.

Auto-merge — when is it OK

GitHub's auto-merge means "merge automatically once all required checks pass and required approvals are complete." It's risky, but for certain change types it's safe.

Auto-merge OKAuto-merge forbidden
Doc / comment fixesApplication logic
Dependency patch bumps (when CI passes)Schema migrations
Lint / format auto-fixesSecurity / auth logic
Generated type / SDK updatesInfrastructure / IaC
# If it's a dependency PR and CI passes + AI review APPROVE, enable auto-merge
- name: Enable auto-merge for safe deps
  if: |
    contains(github.event.pull_request.labels.*.name, 'dependencies') &&
    github.event.pull_request.user.login == 'dependabot[bot]'
  run: gh pr merge --auto --squash "${{ github.event.pull_request.number }}"

Escalation path

When AI (whether generation or review) is uncertain, it tags a human. It automatically leaves a comment like "this change touches auth logic — needs review by a security owner" and attaches the needs-human label.


Chapter 6 · S5 — CD: Preview → Canary → Production

It's merged. Now, deployment. The key is not deploying everything at once.

Preview deployment — per PR

When a PR opens, deploy to an isolated real environment and give it a unique URL. Vercel, Netlify, and Cloudflare provide this out of the box; for your own infrastructure, implement it with a per-PR namespace.

→ The human reviewer in S4 sees a working artifact, not code. The AI reviewer can also run E2E against the Preview URL.

After merge — progressive delivery

merge to main
Canary deploy (5% of traffic)
   ▼  [S6 auto-verification]
smoke test + SLO observation (5~15 min)
   ├── fail ──▶ auto rollback (remove Canary)
   ▼ pass
Production promotion (100% of traffic)
  • Canary — only a slice of traffic (5~10%) goes to the new version. Even if a problem hits, the blast radius is small.
  • Feature Flag — deploy the code but keep the feature off. Turning it on is a separate decision.
  • Environment promotion — dev → staging → prod. Each step is a gate.

Implementing the gate with GitHub Environments

name: Deploy
on:
  pull_request:
    types: [closed]
    branches: [main]

jobs:
  deploy-canary:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    environment: canary          # protection rules: none (automatic)
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy.sh canary

  promote-production:
    needs: [deploy-canary, verify-canary]
    runs-on: ubuntu-latest
    environment: production      # protection rules: required reviewers = human gate
    steps:
      - run: ./scripts/deploy.sh production

Put required reviewers on environment: production and human approval is enforced for production promotion — another Human Gate baked right into the workflow.


Chapter 7 · S6 — Auto-verification

Deploying isn't the end. A machine confirms that what was deployed actually works.

Post-deploy verification layers

VerificationWhatWhen
Smoke testA few core paths (login, health check)Immediately after deploy
E2EMajor user scenariosCanary stage
Synthetic monitoringPeriodic pings from outside (Checkly, Datadog)Continuous
SLO observationError rate, latency, availabilityDuring the Canary window
Visual regressionUI pixel diffsPreview/Canary

The verification gate — promote the Canary or roll back

  verify-canary:
    needs: deploy-canary
    runs-on: ubuntu-latest
    outputs:
      verdict: ${{ steps.gate.outputs.verdict }}
    steps:
      - name: Smoke test
        run: ./scripts/smoke-test.sh https://canary.example.com

      - name: Observe SLO for 10 minutes
        id: gate
        run: |
          sleep 600
          ERROR_RATE=$(curl -s "$METRICS_API/error-rate?env=canary&window=10m")
          P99=$(curl -s "$METRICS_API/latency-p99?env=canary&window=10m")
          # fail if error rate exceeds 1% or p99 exceeds 500ms
          if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then
            echo "verdict=rollback" >> "$GITHUB_OUTPUT"; exit 1
          fi
          echo "verdict=promote" >> "$GITHUB_OUTPUT"

  rollback-canary:
    needs: verify-canary
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - run: ./scripts/rollback.sh canary

If verification fails → the Canary is removed automatically. Production traffic was only ever 5% affected, and even that is reclaimed immediately.


Chapter 8 · S7 — Monitoring & the feedback loop (closing the loop)

This is where the pipeline goes from a line to a loop. Even after deployment, the system keeps watching.

Auto-rollback triggers

Observation continues even after production promotion. Break the SLO and it rolls back automatically.

  • Error budget burn rate — if it burns down fast, roll back.
  • Latency spike — p99 exceeds the threshold.
  • Core business metric drop — payment success rate, signup conversion rate, etc.

The self-healing loop — an incident becomes an Issue again

This is the core of the loop. When an auto-rollback happens, the incident is turned into a new Issue and sent back to the pipeline's entrance (S1).

name: Monitor & Auto-Rollback
on:
  schedule:
    - cron: '*/5 * * * *'      # check SLO every 5 minutes
  workflow_run:
    workflows: ["Deploy"]
    types: [completed]

jobs:
  slo-check:
    runs-on: ubuntu-latest
    steps:
      - name: Check production SLO
        id: slo
        run: |
          BURN=$(curl -s "$METRICS_API/error-budget-burn?env=prod&window=1h")
          echo "burn=$BURN" >> "$GITHUB_OUTPUT"

      - name: Rollback + file issue if breaching
        if: ${{ steps.slo.outputs.burn > 2.0 }}
        run: |
          ./scripts/rollback.sh production
          gh issue create \
            --title "Auto-rollback: error budget burn rate ${{ steps.slo.outputs.burn }}" \
            --label "ai-ready,incident,priority-high" \
            --body "Auto-rolled back due to a production SLO violation.
            Last deploy: ${{ github.sha }}
            burn rate: ${{ steps.slo.outputs.burn }}
            The agent should analyze the diff of the rolled-back commit, diagnose the cause,
            and submit a fix PR."

This Issue has the ai-ready label attached, so → S1 triggers again. AI analyzes the rolled-back commit, creates a fix PR, AI review → CI → human gate → redeploy. The loop is closed.

What a closed loop means

   Issue ──▶ PR ──▶ review ──▶ CI ──▶ merge ──▶ deploy ──▶ verify ──▶ monitor
     ▲                                                                  │
     └──────────── incident becomes a new Issue ◀───────────────────────┘

The system takes the problem it created as its own input and fixes it. The human still guards the merge gate, but is freed from the repetitive labor of detection, diagnosis, kicking off the fix, and redeploying.


Chapter 9 · The full implementation — wiring it together with GitHub Actions

The stages so far, as actual files. 5 workflows connected by events.

Workflow map

FileTriggerRole
ai-resolve.ymlissues: labeledS1: Issue → PR
ai-review.ymlpull_request: opened/synchronizeS2: AI review
ci.ymlpull_request: opened/synchronizeS3: verification gate
deploy.ymlpull_request: closed (merged)S5: Canary → Production
monitor.ymlschedule + workflow_runS6/S7: verify, rollback, feedback

How they connect via events

GitHub Actions has no central orchestrator. Events are the connecting wires.

issue.labeled('ai-ready')  ──▶ ai-resolve.yml ──▶ (PR created)
pull_request.opened ◀──────────────────────────────────┘
   ├──▶ ai-review.yml   (AI review → report check)
   └──▶ ci.yml          (verification → report check)
   [branch protection: all checks + human approval awaited]
pull_request.closed(merged) ──▶ deploy.yml ──▶ (Canary → verify → Production)
workflow_run('Deploy' completed) ──▶ monitor.yml       │
schedule('*/5')                  ──▶ monitor.yml ◀─────┘
   └──▶ on SLO violation: rollback + issue.create('ai-ready')
                                          └──▶ (back to ai-resolve.yml — loop complete)

ai-resolve.yml — S1

name: AI Resolve
on:
  issues:
    types: [labeled]

jobs:
  resolve:
    if: github.event.label.name == 'ai-ready'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
      issues: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Skip if PR already exists
        id: dedup
        run: |
          N=$(gh pr list --search "#${{ github.event.issue.number }} in:body" --json number --jq 'length')
          echo "exists=$N" >> "$GITHUB_OUTPUT"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Run agent
        if: steps.dedup.outputs.exists == '0'
        uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: |
            Issue #${{ github.event.issue.number }}: ${{ github.event.issue.title }}

            ${{ github.event.issue.body }}

            Implement this issue. Create a branch, make the changes, get the tests passing,
            and open a Draft PR that includes "Closes #${{ github.event.issue.number }}".
            Follow the CLAUDE.md conventions, and summarize what and why in the PR body.

ci.yml — S3

name: CI
on:
  pull_request:
    types: [opened, synchronize, ready_for_review]

concurrency:
  group: ci-${{ github.event.pull_request.number }}
  cancel-in-progress: true     # cancel the previous run on a new push — saves cost

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint        # fast things first
      - run: pnpm typecheck
      - run: pnpm test
      - run: pnpm build

deploy.yml — S5 (see Chapter 6, essentials only)

name: Deploy
on:
  pull_request:
    types: [closed]
    branches: [main]

jobs:
  canary:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    environment: canary
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy.sh canary

  verify:
    needs: canary
    runs-on: ubuntu-latest
    steps:
      - run: ./scripts/smoke-test.sh https://canary.example.com
      - run: ./scripts/observe-slo.sh canary 600   # observe for 10 min

  production:
    needs: verify
    runs-on: ubuntu-latest
    environment: production       # required reviewers = Human Gate
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy.sh production

  rollback:
    needs: [canary, verify]
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - run: ./scripts/rollback.sh canary

Repo setup checklist

  • Secrets: ANTHROPIC_API_KEY, deployment credentials.
  • Environments: canary (automatic), production (required reviewers designated).
  • Branch protection (main): include ci/verify and ai-review in required checks. Required approvals 1+.
  • Labels: ai-ready, ai-generated, needs-human, incident, dependencies.

Chapter 10 · The decision-gate matrix — what to automate and what the human handles

The real design of a pipeline is "how far do you automate." You set different gates per change type.

Change typeAI generationAI reviewCIHuman gateAuto-mergeDeploy
Docs / commentsoptional✅ possibleautomatic
Dependency patch bump✅ requiredoptional✅ conditionalCanary automatic
Bug fix (narrow scope)✅ 1 personCanary → auto-promote
Feature addition✅ 1+ personCanary → human promote
Refactoring✅ + coverage✅ 1 personCanary → auto-promote
DB migration⚠️ AI assists✅ requiredhuman-triggered
Security / auth logic⚠️ AI assists✅ 2 peoplehuman-triggered
Infrastructure / IaC⚠️ AI assists✅ plan diff✅ requiredhuman-triggered

Legend: ✅ automatic / ⚠️ AI assists only, human leads / ❌ not done

How to climb the trust ladder

Start with every cell conservative — all auto-merge off, all deployments human-triggered. Then raise one cell at a time with data:

  1. Turn on auto-merge for doc PRs for a month → 0 incidents → keep it.
  2. Turn on auto-merge for dependency bumps → watch the CI pass rate and rollback rate → if stable, keep it.
  3. Turn on Canary auto-promotion for bug fixes → watch the escape rate (missed bugs).

Do not widen the automation scope without success-rate data. Trust is accrued, not declared.


Chapter 11 · Safety mechanisms & failure modes

The faster the automation, the faster the accidents. Put a defense on each failure mode.

The "AI approves AI" problem

The most dangerous anti-pattern. If the generation agent reviews and approves its own PR, verification is zero. Defenses:

  • Generation and review are different tools/models.
  • An AI review's APPROVE cannot replace human approval — the "required approval" in branch protection must be a human account.
  • Configure bot account approvals to be excluded from the required approval count.

Prompt injection through the pipeline

Issue bodies, PR comments, code, CI logs — all of it is input to the agent. An attacker can plant commands there.

  • No auto-triggering from external users' Issues/PRs — only the ai-ready label applied by a member triggers.
  • Minimal-privilege agent tokens — no access to production or secrets.
  • Detect workflow file changes — if a PR touches .github/workflows/, it always requires a mandatory human review.

Cost runaway

  • Step ceilings, a self-heal attempt ceiling (3 times).
  • Eliminate duplicate runs with concurrency + cancel-in-progress.
  • Limit the number of concurrently running agents.
  • Daily/weekly cost alarms.

Every stage is fail-closed + rollback

StageOn failureRollback means
S1 generationNo PR created, comment on the issue
S2 reviewREQUEST_CHANGES, merge blocked
S3 CIRed light, merge blocked
S4 humanNot merged
S5 deployHalt at the Canary stagerollback.sh canary
S6 verificationNot promotedRemove the Canary
S7 monitorAuto rollback + filed as an issuerollback.sh production

Auditing — who, when, what

Every stage's actions must be recorded as logs. What AI did must be traceable via the commit trailer (Co-Authored-By:), the PR label (ai-generated), and the workflow run history. If you can't answer "why did this get deployed," it's better to turn that automation off.


Chapter 12 · Operations — metrics and gradual trust

Turning the pipeline on isn't the end. You measure and tune.

Metrics to track

MetricMeaningHealthy direction
Lead time (Issue→deploy)Time for one lap
% auto-mergedShare merged without a human↑ (but watch the escape rate)
AI review precisionShare of AI review flags that are actually valid
Escape rateShare of bugs that got through the pipeline↓ (most important)
Rollback rateShare of deployments rolled backlow and stable
Self-heal success rateShare of CI failures AI fixed
Human review wait timeS4 queue wait

Applying DORA metrics to this pipeline

The traditional 4 DORA metrics apply directly — Deployment Frequency, Lead Time, Change Failure Rate, MTTR. The goal of an AI pipeline is to raise Throughput without breaking Stability. If the Change Failure Rate rises, shrink the automation scope.

The bottleneck is almost always S4

Even if 10 agents create 10 PRs in 5 minutes, if human review handles 5 a day, throughput is 5 a day. The real ceiling is review capacity, not generation speed. So the focus of tuning is:

  • Strengthen AI review (S2) to reduce the human review burden.
  • Split PRs smaller to make review faster.
  • Increase auto-merge for low-risk changes (while watching the data) to route around S4.

Gradual trust — climb the ladder with data

Look at the metrics every week. If the escape rate is low and stable → climb one rung of the trust ladder (Chapter 10). If the escape rate rises or the rollback rate spikes → step down one rung. The automation scope is not a fixed value but a dial you adjust with data.


Epilogue — the pipeline is the shape of the team

Once you implement this post's pipeline fully, the way the team works changes.

  • Developers type less code and write Issues well and review PRs well.
  • Touch-up reviews go to AI, judgment reviews go to humans.
  • The repetitive labor of detection, diagnosis, kicking off the fix, and redeploying goes to the system.
  • Humans guard the gate of irreversible decisions.

Summed up in three core insights.

  1. It's a loop, not a line. Deployment is not the end. When monitoring catches an incident and turns it back into an Issue, the system takes its own problem as input and fixes it.

  2. "Automate the work, gate the decisions." Let AI write, review, and deploy code — but for irreversible decisions like merge, schema, and security, put a human or a strong automated check in the way. Fail-closed is the default.

  3. Trust is accrued. Don't automate everything from the start; start with low-risk work and climb one rung at a time, watching the metrics (especially the escape rate). The automation scope is a dial you adjust with data.

Paradoxically, the ultimate purpose of all this automation is not to take humans out of the work, but to focus humans on the most important place — judgment and decisions. When the pipeline absorbs the repetitive labor, the team's thinking rises from "how do we build it" to "what and why do we build."

A 14-item checklist

  1. Have you identified all 7 stages and assigned an owner to each?
  2. Is every stage fail-closed?
  3. Does every stage have a rollback/cancel means?
  4. Are the generation agent and review agent separated (different models)?
  5. Is AI review wired in as a required status check?
  6. Are CI failure messages friendly enough for AI to read?
  7. Does the self-heal loop have an attempt ceiling?
  8. Does branch protection enforce required checks + human approval?
  9. Is human approval counted only from human accounts, not bots?
  10. Are Preview, Canary, and Production separated in stages?
  11. Does the production environment have required reviewers?
  12. Does auto-verification (smoke, SLO) gate the Canary promotion?
  13. On an SLO violation, do auto-rollback + auto Issue creation happen (the loop)?
  14. Do you track the escape rate and adjust the automation scope with that data?

10 anti-patterns

  1. The generation agent reviews and approves its own PR.
  2. An AI review APPROVE counted as a human approval.
  3. Auto-merging application logic.
  4. 100% production deploy the moment a merge happens (no Canary).
  5. No post-deploy verification (no smoke / SLO gate).
  6. Auto-rollback exists but the incident doesn't come back as an Issue (the loop isn't closed).
  7. CI failure messages are unfriendly, so AI self-healing is impossible.
  8. No ceiling on the self-heal loop → cost bomb.
  9. External users' Issues auto-trigger the pipeline.
  10. Widening the automation scope without measuring the escape rate.

Next post preview

Candidates for the next post: Building your own AI code reviewer — a custom review bot that learns the team's conventions, Progressive Delivery deep dive — SLO-based auto-promotion with Argo Rollouts and Flagger, Agent orchestration — turning a multi-stage pipeline into a state machine with LangGraph.

"The best pipeline doesn't replace humans. It absorbs all the rest of the repetition so that humans only make decisions."

— From Issue to Deploy, building an automation pipeline, done.