시도 횟수만 증가
실패의 정체와 현재 commit을 저장하지 않으면 같은 원인을 다른 시도로 포장합니다.
ADR / RECOVERY CONTROL
bounded repair loop는 실패를 다시 시도하는 기능이 아니라, 같은 실패를 식별하고 진전이 없으면 사람에게 넘기는 상태 머신입니다.
01 / PROBLEM
CI 실패나 review comment가 들어올 때마다 자동 수정이 같은 PR에 patch를 밀어 넣으면, 원래 실패가 해결됐는지 새 실패가 생겼는지 구분하기 어려워집니다. 이벤트가 다시 loop를 깨우고, loop가 다시 push하면서 비용과 diff가 커집니다.
단순한 “최대 5회” 같은 라벨·카운터는 여러 실행 주체가 공유하지 못하거나, 프로세스가 죽은 뒤 상태를 잃어 중복 PR을 만들 수 있습니다.
02 / LIMITATION
실패의 정체와 현재 commit을 저장하지 않으면 같은 원인을 다른 시도로 포장합니다.
label은 durable한 실행 계약과 중복 억제 키를 표현하기에 너무 약하고, local babysitter와 Actions가 경쟁합니다.
failure signature가 그대로인데도 재진단하면 토큰·CI·PR만 소모하고 실제 진전은 없습니다.
03 / SOLUTION
# lib/repair_coordinator.py
MAX_REPAIR_ATTEMPTS = 2
RepairState(
parent_pr, current_pr, attempt,
failure_signature, run_id, commit_sha
)
repair_key = f"parent_pr:attempt:failure_signature"lib/repair_coordinator.py가 모델의 진단·patch와 분리된 durable 경계를 소유합니다. failure_signature는 category, checks, findings, evidence를 정렬해 SHA-256으로 만들고, has_progress는 signature 변경·finding 감소·성공 check 증가 중 하나가 있는지 봅니다.
상태는 parent_pr, current_pr, attempt, failure_signature, run_id, commit_sha를 함께 저장합니다. 따라서 local babysitter와 GitHub event adapter가 같은 coordinator를 깨워도 repair_key로 동일한 시도를 중복 생성하지 않습니다. attempt 0 이후 repair PR은 최대 2개이며, 무진전이면 human_exception evidence bundle로 종료합니다.
ADR TAKEAWAY
각 patch에는 focused verification, full verification, commit SHA, append-only event가 붙습니다. 최종 merge는 자동화하지 않고 human hand-off로 남깁니다.