CI/CD 통합
Stripe Minions 워크플로우
섹션 제목: “Stripe Minions 워크플로우”Stripe의 “Minions” 시스템은 AI 에이전트 기반 CI/CD의 가장 잘 알려진 사례입니다. 이 시스템은 주 1,000개 이상의 PR을 자동으로 머지합니다.
워크플로우는 단순합니다:
1. 개발자가 태스크 설명 작성 ↓2. 에이전트가 코드 작성 ↓3. CI 파이프라인 자동 실행 (테스트, 린트, 타입체크) ↓4. CI 통과 → 에이전트가 PR 오픈 ↓5. 인간이 코드 리뷰 후 머지이 워크플로우의 핵심은 CI가 에이전트의 품질 게이트 역할을 한다는 점입니다. 에이전트가 아무리 자신 있게 작성한 코드라도 CI를 통과해야만 PR로 제출됩니다.
Pre-commit Hooks 설정
섹션 제목: “Pre-commit Hooks 설정”Pre-commit hooks는 에이전트가 커밋하기 전에 자동으로 실행되는 검증 단계입니다.
.pre-commit-config.yaml
섹션 제목: “.pre-commit-config.yaml”repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-json - id: check-yaml
- repo: local hooks: # TypeScript 타입 체크 - id: typescript-check name: TypeScript Check entry: pnpm tsc --noEmit language: system types: [ts, tsx] pass_filenames: false
# 테스트 실행 (변경된 파일 관련) - id: vitest-related name: Run Related Tests entry: pnpm vitest run --reporter=verbose language: system pass_filenames: false
# console.log 감지 - id: no-console-log name: No console.log entry: grep -rn "console\.log" src/ language: system pass_filenames: false
# TODO 주석 감지 - id: no-todo-comments name: No TODO Comments entry: grep -rn "TODO\|FIXME\|HACK" src/ language: system pass_filenames: falseGitHub Actions 파이프라인
섹션 제목: “GitHub Actions 파이프라인”기본 에이전트 검증 워크플로우
섹션 제목: “기본 에이전트 검증 워크플로우”name: Agent Output Verification
on: pull_request: branches: [main, develop]
jobs: verify: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20'
- name: Install pnpm uses: pnpm/action-setup@v3 with: version: 9
- name: Install dependencies run: pnpm install --frozen-lockfile
- name: TypeScript Check run: pnpm tsc --noEmit
- name: Lint run: pnpm eslint src/ --max-warnings 0
- name: Tests run: pnpm vitest run --coverage
- name: Coverage Check run: pnpm vitest run --coverage --coverage.thresholds.lines=80
- name: Build run: pnpm build
- name: Structural Constraints Check run: pnpm test:arch구조적 제약 테스트 (ArchUnit 스타일)
섹션 제목: “구조적 제약 테스트 (ArchUnit 스타일)”에이전트가 아키텍처 경계를 위반하지 않도록 코드 구조 자체를 테스트합니다.
import { describe, it, expect } from 'vitest'import { glob } from 'glob'import * as fs from 'fs'
describe('Architecture Constraints', () => { it('agents/ 폴더는 tools/ 만 import 할 수 있다', async () => { const agentFiles = await glob('src/agents/**/*.ts')
for (const file of agentFiles) { const content = fs.readFileSync(file, 'utf-8') const imports = content.match(/from ['"]\.\.\/([^'"]+)['"]/g) ?? []
for (const imp of imports) { const isAllowed = imp.includes('../tools') || imp.includes('../types') expect(isAllowed, `${file}: 허용되지 않은 import: ${imp}`).toBe(true) } } })
it('프로덕션 코드에 console.log가 없어야 한다', async () => { const srcFiles = await glob('src/**/*.ts', { ignore: 'src/**/*.test.ts' })
for (const file of srcFiles) { const content = fs.readFileSync(file, 'utf-8') expect( content.includes('console.log'), `${file}: console.log 발견` ).toBe(false) } })
it('모든 exported 함수는 JSDoc이 있어야 한다', async () => { const files = await glob('src/harness/**/*.ts')
for (const file of files) { const content = fs.readFileSync(file, 'utf-8') const exportedFunctions = content.match(/^export (async )?function \w+/gm) ?? [] const jsdocBlocks = content.match(/\/\*\*[\s\S]*?\*\//g) ?? []
expect( jsdocBlocks.length >= exportedFunctions.length, `${file}: export 함수 ${exportedFunctions.length}개, JSDoc ${jsdocBlocks.length}개` ).toBe(true) } })})에이전트 PR 자동화
섹션 제목: “에이전트 PR 자동화”에이전트가 작성한 PR에 자동으로 레이블을 붙이고 추가 검증을 실행하는 워크플로우입니다.
name: Label Agent PRs
on: pull_request: types: [opened]
jobs: label: runs-on: ubuntu-latest steps: - name: Check if agent-authored id: check-agent run: | BODY="${{ github.event.pull_request.body }}" if echo "$BODY" | grep -q "\[agent-generated\]"; then echo "is_agent=true" >> $GITHUB_OUTPUT fi
- name: Add agent label if: steps.check-agent.outputs.is_agent == 'true' uses: actions/github-script@v7 with: script: | github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, labels: ['agent-generated', 'needs-human-review'] })
- name: Require additional review for agent PRs if: steps.check-agent.outputs.is_agent == 'true' uses: actions/github-script@v7 with: script: | github.rest.pulls.requestReviewers({ owner: context.repo.owner, repo: context.repo.repo, pull_number: context.issue.number, reviewers: ['senior-engineer-username'] })CI 실패 피드백 루프
섹션 제목: “CI 실패 피드백 루프”에이전트가 CI 실패를 자동으로 인식하고 수정하도록 피드백 루프를 구성합니다.
CI 실패 발생 ↓실패 로그를 에이전트 컨텍스트에 주입 ↓에이전트가 수정 시도 (최대 3회) ↓성공 → PR 업데이트실패 → 인간 에스컬레이션async function handleCIFailure( failureLog: string, agent: Agent, maxRetries: number = 3): Promise<'fixed' | 'escalated'> { for (let attempt = 1; attempt <= maxRetries; attempt++) { const context = `CI 실패 (시도 ${attempt}/${maxRetries}):\n\n${failureLog}` const result = await agent.run(context)
if (await runCI()) { return 'fixed' } }
await notifyHuman('에이전트가 CI를 3회 시도 후 실패. 수동 개입 필요.') return 'escalated'}CI/CD는 에이전트의 외부 검증자입니다. Pre-commit hooks로 커밋 전 검증, GitHub Actions로 PR 품질 게이트, 구조적 제약 테스트로 아키텍처 무결성을 유지하세요. Stripe Minions처럼 CI 통과를 에이전트 작업의 완료 기준으로 정의하면, 인간 리뷰어는 비즈니스 로직에만 집중할 수 있습니다.