Jira에 티켓 적고 산책 갔다 왔더니 PR이 만들어져 있었다 — 개발 자동화 기록

Jira 티켓 적고 산책 7분 뒤 PR — jira-agent 자동화 기록

Jira에 KAN-9 티켓 하나 적고 산책 갔다 왔더니 PR이 만들어져 있었다. 정확히는 16시 44분에 enqueue, 16시 51분에 done. 7분 5초. polling 5분 빼면 실제 처리는 2분 안쪽이다.

Jira 티켓을 적으면 M4 Mac Studio에 상주하는 워커가 그걸 받아서 코드 변경, 테스트 작성, GitHub PR 생성, CI 통과 확인까지 자동으로 한다. 사람이 개입하는 건 두 군데뿐이다. 티켓 작성과 PR 리뷰·머지. 그 사이는 모두 자동이다.

비슷한 도구가 이미 있다. Cursor는 IDE 통합형으로 시작했지만 2025년부터 Background Tasks로 비동기 영역에 들어왔다. Devin은 풀스택 자율 SaaS다. 둘 다 잘 만든 도구다.

그래도 jira-agent를 직접 만들 이유가 있었다. 첫째, Jira-driven이라 IDE도 Cursor 화면도 안 켜도 된다. Jira 티켓에 스펙만 적으면 5분 안에 워커가 가져간다. 둘째, 로컬 LLM 우선이다. 기본 백엔드가 Qwen3.6 35B (Ollama), 무료. 유료 백엔드(Claude, Kimi)는 옵션이다. Cursor Background Tasks는 cloud 인프라 + Cursor 구독료가 기본이고, Devin은 SaaS 비용이 든다.

흐름은 8 action 시퀀스

Jason         Jira Cloud         M4 Mac Studio              GitHub
─────         ──────────         ─────────────              ──────
티켓 작성  ─→  description    
              labels=ai-impl  
                              ─→ worker (launchd 상주)
                                  └─ polling 5분
                                  └─ 8 action 시퀀스
                                     ├─ spec_validate
                                     ├─ branch_create
                                     ├─ impl (LLM)
                                     ├─ test_write (LLM)
                                     ├─ test_run_local (red-green)
                                     ├─ push
                                     ├─ pr_create
                                     └─ ci_monitor
                              ←─ comment "✅ ok"

핵심은 TDD red-green 검증이다. test_write 단계가 끝나면 새로 쓴 테스트가 실제로 fail하는지 확인한다 (red 단계). impl을 stash해서 테스트만 돌렸을 때 fail이 안 나면 거부한다. 가짜 테스트 차단이다.

KAN-9 사례

티켓 본문은 이렇게 적었다.

## Target File
src/utils/version_bump.py

## Related Files
- src/utils/version_bump.py
- src/utils/_semver.py

## Acceptance Criteria
- _semver.py has parse_semver(version: str) -> tuple[int, int, int]
- version_bump.py has bump_version(version: str, kind: str) -> str
- bump_version("1.2.3", "patch") returns "1.2.4"
- bump_version with bad kind raises ValueError

## Test Examples
- tests/test_version_bump.py::test_parse_semver_returns_tuple
- tests/test_version_bump.py::test_bump_patch

워커가 처리한 Jira 코멘트 로그는 이랬다.

🤖 [spec_validate] ✅ ok (0.5s)
🤖 [branch_create] ✅ ok (0.5s) — branch=jira-agent/KAN-9
🤖 [impl] ✅ ok (66s) — files=[src/utils/version_bump.py, src/utils/_semver.py]
🤖 [test_write] ✅ ok (28s)
🤖 [test_run_local] ✅ ok (0.5s)
🤖 [push] ✅ ok (1.8s)
🤖 [pr_create] ✅ ok (2.1s) — PR #5
🤖 [ci_monitor] ✅ ok (22.6s)

3 파일이 PR에 들어갔다. version_bump.py 32줄, _semver.py 22줄, test_version_bump.py 30줄. 총 84줄. 티켓 적은 뒤로 남은 사람 작업은 PR 머지뿐이었다.

중단 규칙

워커가 무한루프나 가짜 완료에 빠지지 않도록 3가지 중단 규칙이 들어 있다.

  • loop_no_progress: LLM이 tool call 50회 넘게 돌았는데 worktree diff가 0이면 blocked로 떨어뜨린다. 무한루프 멈춤.
  • fake_done_no_evidence. impl이 ok로 끝났는데 changed_files가 비어있으면 가짜 완료로 처리한다.
  • stale_blocker: blocked 상태가 30분 지나면 Jira 코멘트 + macOS 알림을 보낸다.

원래는 cost_cap_exceeded도 들어 있었다. LLM 비용 누적이 한도 넘으면 멈추는 규칙이다. 다만 Qwen이 cost_usd를 보고하지 않아서 현재 운영에서는 작동할 일이 없다. 무료 로컬 LLM만 쓴다면 빼도 된다. Claude·Kimi 같은 유료 backend로 갈 사람은 처음부터 넣어두는 게 좋다.

한계

  • loop_no_progress 실 트리거가 production에서 0회. unit 테스트로만 검증됐다. 작동 검증은 paid backend 단계에서 한다.
  • Qwen 품질은 L1~L5 단순 task만 검증됐다. L6(KAN-9 multi-file)까지는 됐지만, 복잡한 비즈니스 로직이나 라이브러리 깊은 호출은 미검증이다.
  • mDNS 의존. M4 워커는 jaeseung-ui-MacStudio.local로만 접근된다. 외부망에서 안 됨. webhook도 Cloudflare Tunnel 없이는 불가.

다음 단계

지금 검증 범위는 Qwen 무료 backend의 L1~L5 task다. L6 multi-file은 KAN-9 한 번 됐다. 다음 검증은 paid backend로 복잡한 비즈니스 로직을 돌리는 단계다.


참고

댓글

이 블로그의 인기 게시물

Claude Opus 4.7 출시 총정리 — 뭐가 달라졌고 지금 써야 하나

맥 스튜디오 M4 Max 128GB 로컬 LLM 4개 속도 비교 — gemma4·llama3.3·qwen3 실측

Claude Code로 블로그 발행 15분을 1줄로 — 해고 후 첫 자동화 경험