Gemini가 Claude의 PR을 리뷰했다. 서른여섯 개의 코멘트 끝에, 코드는 더 나아졌다.
언제나 작성자에게 동의하는 리뷰어는 리뷰어가 아니다. AI가 쓴 코드를 경쟁 AI 앞에 세워보라. 사각지대가 드러난다 — 그대로 프로덕션에 배포됐을 스레드 안전성 버그까지 포함해서.
내가 계속 돌아오게 되는 패턴은 Claude가 코드를 작성하고 Gemini가 그것을 리뷰하는 패턴이다. 서로 다른 학습 데이터. 서로 다른 사각지대. 무엇이 방어 가능한 패턴이고 무엇이 냄새나는 코드인지에 대한 서로 다른 견해. 이번 토요일은 이 패턴이 옳다고 생각하는 이유를 가장 구체적으로 보여준 사례였다.
지난 한 주 동안 API 전반에 걸쳐 Phase-2 엔드포인트 확장 PR 묶음이 들어왔다. 여덟 개의 개별 서브시스템이 각각 공개 표면을 확장받았다. 구현의 대부분은 Claude가 작성했다. 어느 것도 머지하기 전에 나는 그것들을 Gemini의 리뷰 패스에 통과시켰다. Gemini는 이 묶음 전체에 걸쳐 서른여섯 개의 구체적인 코멘트를 남겼다.
나는 그 하나하나를 다 검토했다. 이 글은 Gemini가 무엇을 잡아냈는지, 그리고 그 포착이 어떤 종류의 포착이었다는 것이 무엇을 의미하는지에 대한 글이다.
여덟 개 서브시스템이란
이번 PR들은 Phase-2 확장이 필요했던 여덟 개의 API 라우터 모듈을 다루었다: 애니메이션, 네트워크, 오디오, AI 인지, 씬 constructive-solid-geometry, 입력 액션 및 게임패드 바인딩, XR 앵커 포즈 타입, 스크립팅 Lua VM 라이프사이클. 각 PR은 완전한 요청/응답 스키마, 헬퍼, 테스트와 함께 열두 개에서 마흔 개 사이의 엔드포인트를 추가했다.
이 PR들은 사소하지 않았다. 각각이 공개 표면의 실질적인 확장이었다. 합치면 여러 주 분량의 설계 작업을 에이전트들이 약 두 번의 주말에 걸쳐 구현한 것이었다.
Gemini가 잡아낸 것들
구체적으로 짚고 넘어가고 싶다. 카테고리가 중요하기 때문이다.
애니메이션과 네트워크: 블렌드 트리 참조와 헬퍼. Gemini는 애니메이션 API의 블렌드 트리 헬퍼와 네트워크 API의 토폴로지 헬퍼가 누락된 참조를 처리하는 방식에서 미묘하게 다른 컨벤션을 쓰고 있다는 것을 발견했다. 애니메이션은 None에 해당하는 값을 반환하고 판단을 호출자에게 맡겼다. 네트워크는 예외를 발생시켰다. 둘 다 유효한 패턴이다. 그런데 하루 간격으로 들어온 두 PR 사이에서 이것이 일관되지 않았다. 해결책은 이 둘을 맞추는 것이었다. 우리는 명시적 예외 경로를 택했는데, 이는 누락된 참조를 조용한 null로 전파시키는 대신 API 경계에서 바로 드러내기 때문이다.
오디오: 응답 모델 기본값과 헬퍼. Gemini는 여러 오디오 응답 모델이 선택적 필드에 대해 서로 다른 기본값을 쓰고 있다는 것을 발견했다. 어떤 것은 빈 문자열을 기본값으로, 어떤 것은 None을, 어떤 것은 명시적인 null을 썼다. 이 불일치는 클라이언트 바인딩 계층에서 (각 언어가 각 옵션을 다르게 직렬화하는 지점에서) 혼란스러운 동작을 낳았을 것이다. 해결책은 하나의 컨벤션을 정해서 (Python 타입에서는 None, 와이어에서는 null) 일관되게 적용하는 것이었다.
AI 인지: 핸들 맵과 바인딩. Gemini는 인지 서브시스템의 핸들 맵에서 스레드 안전성 문제를 지적했다. 이 맵은 락 없이 백그라운드 스레드에서 변경되는 동시에 API 요청 스레드에서 읽히고 있었다. 부하가 걸리면 이는 진단하기 매우 어려운 간헐적 맵 손상 버그를 낳았을 것이다. 해결책은 읽기 경로를 흔한 케이스(조회가 삽입보다 압도적으로 많다)에 맞춰 최적화한 reader-writer 락이었다.
씬: 핸들 맵과 CSG 응답. AI 인지에서 발견된 것과 같은 계열의 버그다. Gemini는 씬 서브시스템의 CSG 핸들 맵에서 똑같은 스레드 안전성 문제를 잡아냈다. 해결책도 같은 형태였다: reader-writer 락. 이것이 바로 한 종류의 학습된 눈이 어디서나 잡아내는 부류의 버그다. 그 패턴을 본 적이 있기 때문이다. Claude는 같은 패턴을 두 곳에 써놓고도 눈치채지 못했다.
입력: 액션과 게임패드 바인딩. Gemini는 액션 바인딩 API가 유효하지 않은 액션 ID에 매직 넘버 컨벤션(-1)을 쓰는 반면, 게임패드 바인딩 API는 센티널 구조체 값을 쓰고 있다는 것을 발견했다. 이 불일치는 두 API를 넘나들며 작업하는 개발자가 실수로 잘못된 무효-마커를 쓸 때 미묘한 버그를 낳았을 것이다. 해결책은 두 API 모두에 타입이 지정된 ActionId::Invalid 상수를 도입하고 모든 매직 넘버를 그쪽으로 이관하는 것이었다.
XR: 앵커 포즈 타입과 핸들러 재사용. Gemini는 XR API가 서로 다른 엔드포인트에서 미묘하게 다른 두 가지 포즈 타입을 노출하고 있다는 것을 잡아냈다. 하나는 월드 좌표계, 다른 하나는 앵커-로컬 좌표계였다. 이 차이는 실제로 존재하며 소비자에게 중요한데도 엔드포인트들은 그 차이를 명확히 문서화하지 않았다. Gemini는 타입 시스템이 이 구분을 강제하도록 타입을 분리할 것을 제안했다. 해결책은 WorldPose와 AnchorPose를 암묵적 변환이 없는 별개의 타입으로 도입하는 것이었다.
스크립팅: Lua VM 라이프사이클과 누수. Gemini는 Lua VM이 명확한 해체 경로 없이 요청마다 할당되고 있다는 것을 발견했다. 지속적인 부하 아래에서는 이것이 VM 상태를 주소 공간에 누적시켜 결국 API 서버가 다운될 때까지 누수시켰을 것이다. 해결책은 명시적인 획득/반환 시맨틱스를 갖는 서버 스레드별 VM 풀을 도입하고, 성공이든 실패든 상관없이 요청 완료 시점에 실행되는 해체 경로를 추가하는 것이었다.
이 포착들을 카테고리로 보며 눈치챈 것
세 가지 관찰이 있다.
포착의 대부분은 일관성 포착이었다. Gemini 코멘트의 3분의 2는 “이 컨벤션이 방금 당신이 배포한 다른 서브시스템의 컨벤션과 다르다”는 것이었다. 이것이야말로 단일 모델이 잘 잡아내지 못하는 부류의 포착이다. 각 PR이 고립된 상태로 들어왔고, 그것을 작성한 모델은 다른 PR들을 맥락으로 가지고 있지 않았기 때문이다. 묶음 전체를 가로질러 작업하는 리뷰어는 작성자의 머릿속에 없었던 불일치를 본다.
몇 개의 포착은 진짜 버그였다. 핸들 맵에 관한 스레드 안전성 발견은 실제 버그였다. 그대로 배포됐을 것이다. 간헐적이고 진단하기 어려웠을 것이다. Gemini는 이 묶음에서 두 인스턴스(하나는 AI 인지, 하나는 씬 CSG)를 모두 잡아냈다. “락 없는 공유 가변 맵 = 스레드 안전성 우려”에 대한 패턴 인식을 갖고 있었기 때문이다. 다른 모델, 다른 학습, 무엇을 짚어내도록 보정되었는지가 다르다.
몇 개는 문체적인 것이었고 논쟁이 되었다. Gemini의 모든 코멘트가 옳았던 것은 아니다. 몇몇은 문체 취향에 관한 것이었고, 나는 그것을 반박하거나 사람의 판단을 요청했다. 몇몇 코멘트가 기각되었다는 사실이 이 패턴을 약화시키지 않는다. 오히려 강화한다. 언제나 작성자에게 동의하는 리뷰어는 리뷰어가 아니다.
이것이 하는 일과 하지 않는 일
이것이 하는 일: 단일 모델 리뷰가 놓치는 부류의 버그를 잡는다. 구체적으로는 PR을 가로지르는 일관성 버그와, 다른 데이터로 학습된 리뷰어가 작성자의 학습이 보지 못한 것을 짚어내는 패턴 매칭 포착이다.
이것이 하지 않는 일: 사람의 리뷰를 대체하지 않는다. Gemini의 코멘트는 1차 패스였다. 나는 그 하나하나를 읽었다. 몇 개는 기각했다. 대부분은 수용했다. 최종 머지 결정은 나의 것이었다. 이 패턴은 “Claude가 작성하고, Gemini가 리뷰하고, 사람이 결정한다”이다. “Gemini가 결정한다”가 아니다.
이것이 AI 랩들에게 시사하는 바: 최적화해야 할 지표는 “모델의 코드가 그 모델 자신의 리뷰를 통과하는가”가 아니다. “그 모델의 코드가 다른 벤더 모델의 리뷰를 통과하는가”이다. 이 크로스벤더 지표에서 잘 해내는 에이전트가 내가 진지한 작업에서 신뢰하는 에이전트다.
파트너와 빌더가 여기서 가져가야 할 것
에이전트 주도 워크플로우를 운영하고 있는데 아직 다른 벤더 모델을 통한 PR 리뷰를 돌리고 있지 않다면, 다음 배치에서 시도해보라. 셋업 비용은 작다. 버그 포착률은 결코 사소하지 않다. 오늘의 배치는 그대로 배포됐을 실제 스레드 안전성 버그 두 건을 잡아냈다.
만약 당신이 AI 랩이고 코딩 에이전트를 “다른 벤더 모델을 상대로 한 PR 리뷰”라는 목표에 맞춰 최적화하지 않고 있다면, 고려해보라. 이 지표는 정직하다. 신호는 진짜다. 이 지표에서 잘 해내는 에이전트가 진지한 팀들이 채택하게 될 에이전트다.
파트너십을 위해 엔진을 평가하고 있다면, 멀티벤더 리뷰 패턴은 내가 물어볼 규율의 신호 중 하나다. 의미 있는 모든 PR에 대해 크로스벤더 리뷰를 돌리는 팀은 그렇지 않은 팀과는 다른 팀이다. 코드베이스가 그 차이를 반영한다.
서른여섯 개의 코멘트, 여덟 개의 서브시스템, 하루의 토요일. 이 배치는 오늘 아침보다 나아졌다. 이 패턴은 다시 한 번 제 값을 한다.
다시 빌드하러 간다.
AI 랩들이 리뷰하고 — 그 위에 빌드하는 런타임
RakuAI는 LLM 제작사들이 그 위에서 배포하는 공간 런타임이다. 크로스벤더 리뷰, 정직한 지표, 파트너급 규율. 당신의 모델이 실제 세계 어디에 들어맞는지 확인하라.