에이전트 무리에게 속성 테스트를 맡겨 실제 버그를 찾아내는 워크플로

Lobsters그저께조회 2

코딩 에이전트에게 저장소의 버그를 찾아내는 '기계' 자체를 만들게 하고, 그 기계가 이후 케이스를 스스로 생성하고 검증하도록 돌리는 워크플로가 공개됐다. 핵심은 케이스마다 모델 토큰을 쓰지 않는다는 점이다. 에이전트가 참조 구현과 생성기, 단언을 한 번 작성해 두면 그 뒤로는 원하는 만큼 많은 입력 이력을 거의 무료로 돌려볼 수 있다. 폐쇄형 사이버보안 프로그램에 접근하지 않고 일반 코딩 에이전트만으로 가능하다는 것이 이 접근의 요지다.

방식은 속성 테스트의 고전적 골격을 따른다. 대상 구현과 비교할 단순한 참조 구현을 만들고, 무작위 연산 시퀀스를 뽑는 생성기와 결과를 맞대는 단언을 붙인다. Rust에서는 proptest가 케이스 생성과 단언, 실패 케이스 축소를 담당한다. 고도로 최적화된 구현이라도 Vec 하나와 O(n²) 루프 수준의 참조 모델이면 검증에 충분할 때가 많다.

작성자는 이 플레이북을 Codex에 적용해 서로 다른 정확성 버그 13건을 찾았다고 밝혔다. 10월 7일 기준 업스트림에 13건의 보고서를 올렸고, 12건의 수정안과 2건의 실패 테스트 전용 PR을 자신의 포크에 준비했다. 두 수정안은 서로 다른 diff 렌더러에서 같은 캐리지 리턴 프레이밍 버그를 다룬 것이다. 같은 레시피는 jj, uv, Prometheus, Babel에도 평소 업무 중 백그라운드로 돌아갔다. 10월 6일까지 uv는 export 시 선택적 의존성 활성화, 별도 휠 메타데이터 행의 호환성 태그 결합, 워크스페이스 루트 이중 캐싱, 오버라이드가 선택적 의존성 가드를 잃는 문제 등 4건의 수정을 머지했다. 10월 7일까지 Prometheus는 빈 입력에서 문서화된 NaN 대신 패닉을 내는 BucketQuantile과 스키마 축소 시 카운터 리셋 메타데이터가 사라지는 문제, 2건을 머지했다.

다른 사람에게도 옮겨간다. Frank Noirot는 이 글을 읽고 KittyCAD의 클라우드 동기화 IndexedDB 코드에 스킬을 적용해 2건을 찾았고 둘 다 머지됐다. 쓰기 승인 전에 트랜잭션 커밋을 기다리는 수정과 중단된 커서 트랜잭션 뒤 연결을 닫는 수정이다. 더 앞서 Apollo GraphQL의 Rust GraphQL 라우터에서도 같은 방식을 썼다. Intuit와 Wayfair가 프로덕션에서 쓰고 수천 개의 단위·통합 테스트와 스냅샷 테스트를 갖춘 코드베이스였는데도, 사흘 남짓 백그라운드로 돌린 에이전트가 내부 자료구조와 알고리즘의 엣지 케이스에서 30건 넘는 정확성 버그를 찾아냈다.

리뷰어에게 전달되는 형태는 작고 구체적이다. Codex에서는 diff 파일명의 탭이 인용되지 않아 Git이 엉뚱한 파일을 읽거나, POSIX 백슬래시가 디렉터리 구분자로 해석되는 문제가 나왔다. Prometheus에서는 타임스탬프 10, 11을 쓰고 청크를 다시 읽은 뒤 13을 덧붙이면 10, 11, 12가 되돌아오는 직렬화 버그, 그리고 {a="bc"}와 {ab="c"}가 같은 키로 합쳐져 한 시리즈가 다른 시리즈의 시작 타임스탬프를 갖는 캐시 키 문제가 있었다. uv에서는 아무것도 매칭하지 않는 패턴을 추가하면 이미 추적되던 파일이 캐시 추적에서 사라졌다.

운영 규칙도 함께 제시된다. 탐색용 테스트 묶음은 별도 브랜치에 격리하고, 확정된 버그마다 회귀 테스트와 최소 수정만 담은 독립 PR을 만든다. 캐시된 메타데이터와 재계산 결과를 비교하고, 증분 그래프 알고리즘을 전체 순회와 맞대어 보고, 생성한 값을 직렬화기로 왕복시킨다. 하나의 발견에서 확장해, 캐시 무효화 누락이 나오면 그 상태를 변경하는 모든 경로와 같은 방식으로 관리되는 다른 캐시를 함께 점검한다. 참조 모델로는 평범한 HashMap을 쓴 저장소를 세워 두고 Put, Get, Remove 시퀀스를 양쪽에 돌려 읽기 결과를 비교하는 식이면 충분하다. 키 풀은 작게 유지해야 한다. 무작위 키만 뿌리면 서로 무관한 삽입과 없는 키 조회만 나오므로, 같은 키를 다른 값으로 덮어써 낡은 결과가 드러나게 만들어야 한다.

리뷰어에게는 생성기를 이해하거나 참조 구현을 신뢰하지 않고도 검증할 수 있는 단위 테스트를 준다. 에이전트가 만든 테스트 기계를 통째로 담은 거대한 PR은 지양한다. 보안 이슈로 번질 수 있는 건 회사 보안 프로세스나 프로젝트의 비공개 신고 채널로 처리하고, 공개 이슈와 PR, 탐색 브랜치에는 공개 승인 전까지 재현과 수정을 올리지 않는다. 각 버그는 수정 전에 테스트가 실패하고 수정 후 통과하는지 확인하고, 트리거가 되는 시퀀스와 위반된 계약을 설명한다. 애플리케이션이 통째로 죽는 수준의 재현은 필요하지 않다.

실무적으로 이는 커스텀 자료구조와 알고리즘에 대한 참조 구현·생성기·단언 작성이 사람 손에서 에이전트 무리로 넘어갈 수 있다는 뜻이다. 이미 테스트가 두껍게 깔린 코드베이스에서도 남아 있는 엣지 케이스 버그를 값싸게 훑을 수 있다는 점이 핵심이다.

다만 대상 선택이 중요하다. 정답을 확인하는 방법이 단순한 복잡한 동작, 예컨대 커스텀 자료구조나 쿼리 플래너, 그래프 알고리즘이 적합하다. 어떤 대상에서 아무 발견도 나오지 않으면 우선 커버리지 부족을 의심하고, 캐시 무효화를 일부러 제거해 테스트가 잡아내는지 확인한 뒤 되돌리는 식으로 생성기와 단언을 점검한다. 계약 위반이 명확한 건 회귀 테스트와 수정으로, 모호한 건 논의로 돌린다.