flake 테스트만 재실행하는 루프
에이전트가 CI·로컬에서 같은 테스트가 간헐적으로만 빨개지면, 전체 스위트를 다시 돌리거나 본문을 무작정 고치기 쉽습니다. flake 테스트 에이전트 루프는 “실패하면 무조건 패치”가 아니라 이미 flake로 의심되는 케이스만, 한도 안에서, 격리된 재실행으로 확인한 뒤 이슈로 넘길지를 결정하는 절차입니다.
인접 글과 축을 나눕니다. agent-test-gate는 exit code로 통과/중단을 거는 차단막이고, agent-tdd-loop는 의도적 red→green 작성 루프입니다. 이 글은 재시도 한도 · 격리 · 이슈화 기준만 다룹니다. 요금·플랜·제휴 링크는 없습니다. 근거는 pytest flaky / reruns 관행, Jest retryTimes, GitHub Actions 재실행·quarantine 패턴입니다.
재시도 한도는?
한 줄 답: flake 재시도는 같은 명령·같은 시드(가능하면)·최대 N회(팀 기본 2~3) 로 고정하고, N 안에서도 실패하면 패치 루프가 아니라 이슈/격리 루트로 보냅니다. “초록 나올 때까지”는 한도가 아닙니다.
한도 표:
| 항목 | 권장 | 금지 |
|---|---|---|
| 최대 재시도 | 로컬 에이전트 2, CI quarantine 잡 2~3 | 무한·“될 때까지” |
| 대상 | 이미 실패한 테스트 이름/파일만 | 전체 스위트 재실행 |
| 사이 대기 | 짧은 고정 backoff(또는 없음) | 임의 sleep으로 가리기 |
| 시드/환경 | 가능하면 동일 seed·동일 worker 수 | 매 회 환경 바꿔 “우연히 통과” |
| 성공 시 | 로그에 flake-pass-on-retry k/N 남김 | 침묵하고 Done 처리 |
| N회 실패 | 코드 수정 금지 → 이슈 초안 | 본문 추측 패치 시작 |
에이전트에 고정할 문장 예:
Flake retry policy (this task):
- Only re-run the failed test id(s) listed below — not the full suite
- Max retries: 2 (total runs = 1 initial + 2 retries)
- Same command flags / same cwd / do not change seed unless asked
- On pass after retry: report flake-pass-on-retry and STOP editing product code
- On fail after max retries: do NOT patch; draft an issue (see quarantine rules)
- Never treat “retry until green” as success criteria
실무 팁:
- 한도는 Skill/Rule에 숫자로 둡니다. “조금 더 돌려 봐”는 에이전트가 N을 넘깁니다.
- CI에 이미
retry플러그인이 있으면 에이전트 재시도와 CI 재시도를 합산하지 않습니다—누가 몇 번 돌렸는지 로그에 적습니다. - agent-test-gate와의 한 줄: 게이트는 통과해야 다음 단계, flake 루프는 게이트 실패 중 “간헐”만 별도 정책입니다. 게이트 자체를 느슨하게 만들지 않습니다.
격리 방법은?
한 줄 답: flake 재실행은 실패 케이스만, 공유 상태·병렬 간섭을 줄인 환경에서 돌립니다. 격리는 “테스트 파일을 다른 폴더로 옮긴다”가 아니라 실행 범위·병렬·외부 의존을 좁히는 일입니다.
격리 칸:
| 칸 | 하는 일 | 하지 말 것 |
|---|---|---|
| 선택적 실행 | -k / --testPathPattern / 단일 노드 id | 패키지 전체 test |
| 병렬 | worker=1 또는 해당 파일만 직렬 | flake인데도 max workers |
| 공유 자원 | 임시 DB 스키마·고유 포트·고유 tmpdir | 전역 /tmp/fixed·공유 계정 |
| 시간 | fake clock / 명시 timeout만 | 벽시계에 기댄 sleep 늘리기 |
| 네트워크 | 녹화 픽스처·mock | “한 번 더”로 실서버 호출 |
| 에이전트 범위 | 로그·재현 명령·이슈 초안만 | “관련 코드 리팩터”로 확산 |
복붙 명령 패턴:
# Examples — pick the runner you already use
pytest path/to/test_foo.py::test_bar -vv --maxfail=1
npm test -- --runTestsByPath path/to/foo.test.ts --runInBand
cargo test test_bar -- --exact --nocapture
go test ./pkg -run '^TestBar$' -count=1
# Agent must print before each retry:
# attempt=k/N · cwd=… · command=…
지시 문장 예:
Isolation rules for flake retry:
1. Re-run ONLY: <test ids>
2. Prefer serial / single-worker for this retry loop
3. Do not start unrelated services; use existing mocks/fixtures
4. Do not edit product source to “make the retry pass”
5. Capture full stdout/stderr per attempt into one log blob
agent-tdd-loop와의 한 줄: TDD는 실패를 의도하고 최소 패치로 green이고, flake 격리는 패치 없이 같은 실패를 재현·확인하는 축입니다. 초록이 나와도 제품 코드를 고친 것이 아니면 “수정 완료”로 치지 않습니다.
이슈화 기준은?
한 줄 답: N회 재시도 후에도 실패하거나, 재시도로만 초록이 나오면 제품 패치 대신 이슈(또는 quarantine 라벨) 로 넘깁니다. 에이전트 Done 조건은 “고쳤다”가 아니라 “재현 정보와 이슈 초안이 남았다”입니다.
이슈화 트리거:
| 관찰 | 조치 | Done 정의 |
|---|---|---|
| 1회 실패 → 재시도 모두 실패 | 확정 실패로 보고; flake 루프 종료 | 실패 로그 + 최소 재현 명령 (TDD/게이트 축으로 이관) |
| 실패 후 재시도에서만 통과 | flake 후보 이슈 오픈 | 제목·재현·attempt 로그·의심 축(병렬/시간/IO) |
| 같은 테스트가 주간 k회 flake-pass | quarantine 목록·CI skip/별도 잡 | 라벨 flake / quarantine + 소유 팀 |
| 재시도 중 다른 테스트도 흔들림 | 범위 확대 금지; 별도 티켓 | “추가 flake 발견”만 링크 |
| 에이전트가 추측 패치를 원함 | 거부; 이슈에 가설만 적기 | 코드 diff 없이 종료 |
이슈 초안 템플릿:
Title: [flake] <test id> intermittent failure
Body:
- Repro command: …
- Attempts: fail, fail, pass (or fail×N)
- Env: cwd, runner, workers, seed (if any)
- Suspected axis: parallel | timing | network | shared fs | order-dependent
- Product patch: none in this loop (by policy)
- Next: quarantine / owner / harden test isolation
Labels: flake, testing
운영 규칙:
- flake-pass-on-retry는 머지 면죄부가 아닙니다. PR에 남기되 required gate를 끄지 않습니다(agent-test-gate).
- quarantine은 임시입니다. 이슈 없이 skip만 쌓이면 스위트 신뢰가 무너집니다.
- 에이전트에게 “이슈 말고 그냥 고쳐”만 주면 TDD 루프로 새어 무관 리팩터가 납니다—브리프에 패치 금지를 명시합니다.
한 줄 정리: flake 루프는 실패 케이스만·N회 한도·격리 재실행, 끝에 이슈/quarantine이지, 게이트 완화도 TDD 패치도 아닙니다.
FAQ
재시도 플러그인(CI retry)만으로 충분한가요?
CI 자동 재시도는 신호 손실을 가릴 수 있습니다. 에이전트 루프는 누가·몇 번·어떤 명령으로 재시도했는지 남기고, 반복 flake는 이슈화합니다. 플러그인 ≠ 정책.
agent-test-gate / agent-tdd-loop와 무엇이 다른가요?
agent-test-gate는 통과/중단 차단막이고, agent-tdd-loop는 의도적 red→green 작성입니다. 이 글은 간헐 실패를 한도·격리·이슈로 다루는 flake 전용 재실행 루프입니다.
N=1(재시도 없음)이 더 낫지 않나요?
신규·결정적 실패에는 N=0~1이 맞습니다. flake 정책은 이미 간헐로 분류된 케이스에만 씁니다. 모든 빨강에 재시도를 켜면 게이트가 무너집니다.
재시도로 초록이면 이슈를 안 열어도 되나요?
기본은 엽니다(또는 quarantine 큐에 넣습니다). “한 번 통과”는 재현 가능성이 남은 신호입니다. 침묵 Done은 금지합니다.
출처 (Sources)
- pytest documentation — 선택 실행·실패 출력·플러그인 재시도 관행
- Jest — retryTimes — 테스트 단위 재시도 API
- GitHub Actions Docs — 잡 재실행·CI에서 실패 신호 다루기
- 인접: agent-test-gate(통과/중단 게이트), agent-tdd-loop(작성 red→green), agent-eval-checklist(머지 전 eval)