AI 코딩 에이전트가 완성된 듯 보이게 하는 편법 포착
출처: Dev.to

그린 테스트 스위트는 변경 사항이 정상 동작한다는 뜻이어야 합니다. 실제로는 그렇지 않죠. 테스트를 겨우 통과하도록 약화시킬 수도 있고, 오류를 잡아내어 버릴 수도 있습니다. 리네임이 중간에 멈추어도 컴파일은 됩니다. 이런 경우는 빨간색으로 표시되지 않으며, 대부분의 팀이 이미 사용하고 있는 린터에서도 드러나지 않습니다.
Swarm Orchestrator는 AI가 작성한 풀 리퀘스트에서 바로 그와 같은 문제를 잡아내도록 설계되었습니다.
두 부분
- AI가 만든 PR을 “완료된 것처럼 보이게” 만든 단축키(11가지 체크)를 감사합니다.
- 사용자가 정의한 계약에 따라 패치를 게이트합니다: 빌드가 성공하고, 테스트를 통과하며, 요구 사항을 만족하고, 이를 깨뜨리려는 파괴자(falsifier)에게도 견뎌야 합니다.
TypeScript, Node 20, ISC 라이선스. 감사 측은 모델 인증 정보 없이 실행됩니다.
린터가 놓치는 틈
Semgrep와 ESLint는 위험한 API와 알려진 악성 코드 패턴을 중심으로 만들어졌습니다. diff가 정직한지 여부는 다른 문제이죠. 이들은 테스트가 수정된 뒤에야 통과했는지, 혹은 catch 블록이 잡은 오류를 조용히 무시했는지는 알려주지 못합니다. 바로 이 부분이 빈틈입니다.
머지된 Cloudflare 풀 리퀘스트 두 사례:
| PR | 발견 | Semgrep + ESLint |
|---|---|---|
workers-sdk#14063 | 함수 이름이 바뀌었지만 일부 호출자는 여전히 옛 이름 사용 | 없음 |
workers-sdk#14132 | 빈 catch 블록이 오류를 숨김 | 없음 |
12개의 레포지토리에서 알려진 악성 PR 72개를 대상으로 했을 때, 위 두 분석기는 한 건만 발견했습니다. 감사자는 67건을 플래그했습니다.
감사자가 확인하는 항목
총 11가지 체크 중 기본적으로 8가지를 실행합니다. 나머지 3가지는 아직 실제 PR에서 유용한 신호를 보여주지 못했기 때문에 기본 비활성화 상태이며, 잡음이 많은 체크가 없는 것보다 더 나쁩니다.
기본 체크 항목 예시:
- 잡힌 오류를 무시함
- 중단된 리네임
- 테스트 커버리지 감소
- 약화된 테스트
- 제거된 어설션
- 새
@ts-ignore혹은eslint-disable주석 - 코드 변경 없이 테스트 전용 수정
- 존재하지 않는 모듈을 가리키는 목(mock)
측정된 결과, 가정이 아닌
탐지율은 추정이 아닙니다. 알려진 결함을 실제 PR에 삽입한 뒤 감사자를 실행해 확인합니다. 300건 중 253건을 잡아냈으며, 탐지율은 **84 %**입니다.
재현 방법:
npm run benchmarks:full
런타임 모드 (선택 사항)
체크를 단순히 diff만 읽는 것이 아니라 코드를 실행하도록 할 수도 있습니다: 변이 테스트, 커버리지 측정, 보고된 이슈 재현 등.
예를 들어 trpc#6098에서는 나중에 핫픽스로 수정된 라인에 살아남은 변이가 발견되었습니다. 테스트는 통과했지만 실제로는 해당 코드를 실행하지 않았습니다.
왜 이 모드가 선택 사항인지
코드를 실행하면 잡음이 늘어납니다. 깨끗한 PR에서도 평균 3.4건의 발견이 나오죠. 의도적으로 탐색할 때는 괜찮지만, 기본값으로 켜두면 너무 많은 잡음이 발생하므로 옵트인 방식으로 제공됩니다.
계약으로 “완료” 정의하기
두 번째 명령은 swarm run입니다. 완료가 무엇인지 정의합니다:
obligations:
- type: build-must-pass
command: npm run build
- type: test-must-pass
command: npm test
패치는 모든 의무가 통과하고 파괴자가 이를 깨뜨릴 수 없을 때만 받아들여집니다. 기본 제공자는 결정론적이어서 동일 입력에 대해 동일 결과를 반환하고, 모든 입력과 해시가 해시 체인 형태의 원장에 기록됩니다.
머지 차단
기본적으로 발견은 권고 수준입니다. 게이트 모드에서는 재현 가능한 증거가 있을 때만 머지를 차단할 수 있습니다. 구조적 체크는 너무 많은 오탐을 일으키므로 자동 차단용으로는 신뢰하기 어렵습니다.
현재 실사용 사례에서 충분히 입증된 런타임 신호가 없기 때문에 자동 거부는 제공되지 않으며, 대신 해당 사실을 직접 보고합니다.
대상 독자
AI가 만든 풀 리퀘스트를 많이 검토하고, 일반 린터가 놓치는 신호(테스트를 속이는 징후 등)를 원한다면 바로 이 도구를 위해 설계되었습니다. 또한 --emit-aibom 옵션으로 CycloneDX‑ML 및 SPDX AI BOM 문서를 출력하고, TypeScript와 JavaScript를 지원하며, 오프라인에서도 동작합니다.
리뷰어에게 검토 가치가 있는 코드를 알려 주지만, 버그가 없다고 보증하지는 않습니다.
moonrunnerkc / swarm-orchestrator
AI 코딩 에이전트가 “완료된 것처럼 보이게” 하는 단축키(완화된 테스트, 오류 무시, 가짜 리네임 등)를 검토합니다. 기본적으로 인간에게 플래그를 표시하고, 옵션을 켜면 머지를 차단합니다. 목표를 체크리스트로 바꾸어 모든 체크가 통과해야만 패치를 받아들일 수도 있습니다.
이 도구가 하는 일
Swarm Orchestrator는 풀 리퀘스트 diff를 읽고 AI 코딩 에이전트가 “완료된 것처럼 보이게” 만든 단축키(완화된 테스트, 어설션 제거, 오류 무시, 가짜 리네임 등) 11가지를 플래그합니다. 삽입된 치트를 대상으로 한 벤치마크에서는 300건 중 253건(84 %, 이전 버전 대비 20.5 % 상승)을 복구했으며, 실제 머지된 Cloudflare PR에서도 Semgrep와 ESLint 보안 규칙이 놓친 두 건을 오프라인에서도 재현 가능한 형태로 잡아냈습니다. 발견은 기본적으로 권고 수준이며, 옵션을 켜야만 머지를 차단합니다.
대상 독자
- 대량의 AI‑작성 PR을 검토하면서 일반 린터가 제공하지 않는 “이 변경이 테스트를 속이고 있을지도 모른다”는 신호가 필요한 경우.
- … (이하 생략)
