S4 · 적대 리뷰
구조·품질·원문 대조, 세 시선이 서로의 영역을 안 보고 동시에 읽습니다. 판정자 하나가 그 지적을 걸러 수정 계획을 씁니다.
S4는 앞 단계가 만든 테스트 케이스를 «흠을 잡으려고» 다시 읽는 단계입니다. 세 시선이 서로의 영역을 안 본 채 동시에 읽고, 판정자 하나가 그 지적을 걸러 «프로그램이 그대로 고칠 수 있는» 수정 계획을 씁니다.
「적대적 리뷰」라는 이름은 이 태도에서 왔습니다. 잘 됐다고 봐주는 검토가 아니라, 틀린 곳을 찾아내려고 읽는 검토입니다.
코드가 먼저 짚고, 세 시선이 읽고, 판정자가 정리합니다
- 코드가 먼저 의심 가는 케이스를 표시해 둡니다 — 완전히 똑같은 케이스, 기획서에 근거가 안 보이는 케이스, 아이템 이름이 빠진 케이스. 표시만 하고, 어떻게 고칠지는 AI가 정합니다
- 세 시선이 동시에 읽습니다 — 구조 / 품질 / 원문 대조
- 판정자 하나가 셋의 지적을 서로 반박시켜 틀린 지적을 떨어내고, 수정 계획을 씁니다
- 리뷰에서 새로 생긴 질문이 있으면, 사람에게 가기 전에 사내 위키에서 한 번 더 찾아봅니다
2번에서 셋이 서로의 결과를 모릅니다. 알면 의견이 모이고, 모이면 같은 걸 같이 놓칩니다.
세 시선은 서로의 영역에 들어가지 않습니다
예전에는 검토를 두 차례로 나눴습니다. 1차는 「빠진 게 없나」, 2차는 「문장이 맞나」. 지금은 차례가 없습니다. 세 시선이 한꺼번에 돌고, 각자 「다른 시선의 영역에 들어가지 마라」는 지시를 받습니다.
| 시선 | 묻는 것 | 보는 것 |
|---|---|---|
| 구조 | 짜임이 맞나 | 분류가 맞나, 정상·부정·예외 배분, 똑같은 케이스 |
| 품질 | 문장이 맞나 | 사람이 그대로 따라 할 수 있나, 「적절히」 같은 흐린 말, 한 케이스에 한 가지만 |
| 원문 대조 | 기획서와 맞나 | 숫자·문구가 원문대로인가, 원문에 있는데 빠진 것, 원문에 없는데 지어낸 것 |
나누는 이유는 예전과 같습니다 — 한 번에 다 시키면 쉬운 쪽만 봅니다.
「빠진 게 없나」는 기획서 전체를 훑어야 알 수 있어서 힘듭니다. 「문장이 어색하다」는 읽다가 바로 보입니다. 같이 시키면 자연히 뒤쪽만 잔뜩 나옵니다. 갈라 놓으면 원문 대조 시선은 문장 얘기를 할 수가 없습니다. 자기 담당이 아니니까요.
지금 구조도 처음엔 세 시선을 하나씩 차례로 돌렸습니다. 셋은 서로의 결과가 필요 없으니 2026년 8월부터 동시에 돌리고, 이 구간이 한 번 돌 때마다 9.9분쯤(여러 번 잰 값의 가운데) 짧아졌습니다.
지적에는 반드시 「처방」이 붙습니다
이게 중요한 설계입니다.
다음 단계(수정)는 AI가 아니라 정해진 대로만 움직이는 프로그램입니다. 판단을 하지 않고, 시킨 대로만 고칩니다.
그래서 지적이 「이 문장이 이상하다」로 끝나면 안 됩니다. 「이 문장을 이렇게 바꿔라」까지 와야 합니다.
세 시선은 지적마다 고칠 방향을 붙입니다. 판정자가 그걸 모아 「몇 번 케이스의 어느 칸을, 지금 적힌 이 글에서, 이 글로」라는 모양의 수정 계획으로 씁니다. 그러면서 지적끼리 부딪혀 보게 해 틀린 지적·겹친 지적·값어치 없는 지적은 버립니다.
| 결과 | |
|---|---|
| 지적만 있음 | 누군가 또 판단해야 함 → 매번 다른 결과 |
| 지적 + 처방 | 그대로 실행 → 재현됨 |
판단하는 자리를 하나로 몰아 두는 것이 목적이고, 그 한 자리가 판정자입니다. 여기저기서 판단하면 어디서 뭐가 바뀌었는지 추적이 안 됩니다.
판정자가 수정 계획을 약속한 형식대로 못 쓰면 한 번 더 시킵니다. 두 번째도 못 쓰면 그 자리에서 멈춥니다.
값을 치른 것 — 「형식을 안 정하면 다음 단계가 죽습니다」
제외 사유를 자유 문장으로 쓰게 뒀습니다. 「이건 이래서 뺍니다」처럼요.
그랬더니 AI마다 다르게 적었습니다. 어떤 건 괄호로 감싸고, 어떤 건 서술형으로 길게 썼습니다.
그리고 다음 단계가 그걸 읽다가 멈췄습니다. 예상한 형태가 아니니까요.
명세를 안 쓴 대가였습니다. 「알아서 적어」는 읽는 쪽이 있는 한 성립하지 않습니다.
「같은 지적이 몇 번째인지」 적힌 목록을 들고 읽습니다
검토에서 나오는 지적 중에는 전에도 나온 것이 많습니다. 그래서 반복되는 지적을 모아 두는 목록이 따로 있고, 그 목록에 승격 절차가 있습니다.
| 같은 지적이 | 그러면 |
|---|---|
| 1~2번 | 그냥 고침 |
| 여러 번 반복 | 규칙으로 승격 — 애초에 그렇게 안 쓰게 만듦 |
왜 이게 필요하냐면 — 매번 잡아서 고치면 매번 비용이 듭니다. 그리고 한 번 안 잡히면 그냥 나갑니다.
반복되는 지적은 「검토로 잡을 것」이 아니라 「애초에 안 생기게 할 것」입니다.
그런데 뭐가 반복되는지는 세어 보기 전에는 모릅니다. 매번 고치고 나면 기억에 안 남으니까요. 그래서 세는 장치를 따로 뒀습니다.
세 시선은 이 목록을 먼저 읽고 들어갑니다. 목록에 오른 실수가 이번에도 나왔는지를 자기 영역 안에서 봅니다.
솔직히 적으면, 리뷰가 이 목록에 직접 적지는 않습니다. 리뷰 규칙서에는 「끝나면 목록에 적어라」가 남아 있지만, 지금 세 시선은 그 규칙서를 받지 않고 「결과 파일 말고는 아무것도 쓰지 마라」는 지시를 받습니다. 목록을 채우는 일은 따로 돕니다 — 사람이 테스트하면서 시트를 손으로 고친 흔적을, 파이프라인이 냈던 최종본과 비교해 후보를 뽑습니다. 목록에 올리는 것은 사람이 시킬 때만 합니다.
여기서 장부도 만듭니다
기획서 규칙과 케이스를 의미로 이어 붙이는 일을 여기서 합니다.
「기계적으로 번호를 맞추는 일」이 아니라 「이 규칙을 저 케이스가 실제로 확인하고 있나」를 판단하는 일이라, AI가 합니다.
리뷰가 새로 만든 질문은 사람에게 가기 전에 한 번 더 찾아봅니다
리뷰를 하다 보면 「기획서만으로는 기대 결과를 못 정하겠다」는 케이스가 새로 생깁니다. 그런 케이스에는 「기획 확인 필요」 표시가 붙고, 나중에 기획자에게 가는 질문 목록에 들어갑니다.
그런데 사내 위키에서 답을 찾아보는 일은 설계 단계에서 한 번만 했습니다. 그 뒤 리뷰에서 새로 생긴 질문은 한 번도 찾아보지 않은 채 사람에게 갔습니다.
2026년 9월부터는 수정을 적용하기 바로 전에, 리뷰가 새로 붙인 질문만 골라 사내 위키에서 다시 찾아봅니다. 수정 전이어야 찾은 답이 케이스의 기대 결과까지 채울 수 있습니다. 확신이 없으면 답을 지어내지 않고 그대로 사람에게 묻습니다.
한계도 있습니다. 리뷰어가 적은 이유 문장이 그대로 검색어가 되어서, 무엇에 대한 질문인지 안 적힌 이유로는 아무것도 못 찾습니다.
여기부터는 자세한 기록입니다
코드가 먼저 후보를 뽑는다 — 완전 중복, 원문에 근거 없는 케이스, 아이템 실명 미병기. 그 위에서 세 렌즈가 병렬로 리뷰한다(구조 · 품질 · 원문 대조). 판정자가 셋의 지적을 서로 반박시켜 수정 계획을 낸다. 렌즈 셋과 판정자는 Sonnet 모델로 돌고, 판정자만 깊게 생각하는 설정(effort high)을 쓴다. 판정자 산출이 형식 검증을 두 번 못 넘으면 런이 실패로 멈춘다(2026-09 기준).
예전엔 1차와 2차가, 지금은 세 렌즈가 서로를 안 본다
예전(v2)에는 리뷰를 두 차수로 나눠 담당 항목을 명시적으로 갈랐다.
| 묻는 것 | 보는 것 | |
|---|---|---|
| 1차 | 빠진 게 없는가 | 커버리지, 분류 정합, 검증 단계 배분 |
| 2차 | 내용이 맞는가 | 문장 품질, 중복, 한 케이스 한 검증점 |
그때도 상대 영역은 명시적으로 안 봤다. 2차는 구조를 다시 안 보고, 1차는 문장 품질을 안 봤다. 2차가 하던 구조 관련 일은 1차 지적이 반영됐는지 1:1로 대조하는 것뿐이었다.
지금(2026-09 기준)은 차수가 없다. 구조 · 품질 · 원문 대조 세 렌즈가 한꺼번에 돌고, 렌즈마다 프롬프트에 「타 렌즈 영역 침범 금지」가 박혀 있다. 렌즈 셋은 입출력이 서로 독립이라 2026-08-09 부터 병렬로 돈다 — 순차일 때보다 렌즈 구간이 런당 중앙값 9.9분 짧아졌다. 차수 사이의 「반영 대조」도 없어졌다. 수정 계획은 판정자 하나가 확정하고, 적용은 S5 의 결정론 코드가 한다.
이렇게 나눈 이유는 단순하다. 한 번에 다 보라고 하면 둘 다 대충 본다. 특히 긴 시트에서는 앞쪽만 꼼꼼하고 뒤로 갈수록 흐려지는데, 축을 좁히면 그게 덜하다.
비율 기준도 걸어 뒀다 — 예전엔 1차가, 지금은 구조 렌즈가 든다. 위험도가 높은 소분류는 부정·예외 케이스가 60% 아래면 지적이 뜬다. 사람이 정상 케이스만 쓰는 쪽으로 쏠리기 때문이다. 이 기준은 리뷰 규칙서에서 뽑은 발췌본에 있다. 규칙서 원본은 체인이 런타임에 읽지 않고, 렌즈 문구는 워크플로 코드에 박혀 있다.
지적에는 처방이 따라야 한다
수정 단계는 판단하지 않는 코더로 설계돼 있다. 처방을 그대로 실행할 뿐이다. 지금(2026-09 기준)은 그 코더가 LLM 이 아니라 결정론 코드다.
그래서 리뷰어는 이슈마다 수정자가 그대로 실행할 수 있는 처방을 반드시 쓴다. “이 부분이 모호합니다”로 끝내면 안 되고, 어느 행의 어느 열을 무엇으로 바꾸라고 적어야 한다. 지금은 렌즈가 지적마다 고칠 방향을 필수로 달고, 판정자가 그걸 「케이스 번호 · 열 · 지금 값 · 바꿀 값」 패치로 확정한다. 지금 값을 같이 적는 건, 적용 때 실제 셀이 그 값과 다르면 코드가 고치지 않고 충돌로 돌려보내게 하려는 것이다.
이 규칙이 없으면 판단이 두 번 일어난다. 리뷰어가 “이게 문제다”까지 판단하고, 수정자가 “그럼 어떻게 고치지”를 다시 판단한다. 두 판단이 어긋나면 고친 결과가 지적과 상관없어지고, 그걸 확인하는 건 또 사람이다.
작업을 끝내고 죽는 실패
여기서 커버리지 원장도 만든다 — 기획서 규칙과 케이스를 의미로 이어 붙이는 일이다.
규칙 전량을 한 번에 뱉게 했더니 출력 토큰 상한에 걸렸다. 매핑은 다 끝내 놓고 출력만 못 하고 죽는다. 세 건에서 났고, 재시도까지 겹쳐 한 건은 88.3분이 통째로 날아갔다.
작업 도중에 죽는 실패보다 작업을 끝내고 죽는 실패가 비싸다. 앞의 것은 부분 결과라도 남는데, 뒤의 것은 100%가 사라지고 그 사실조차 늦게 안다. 지금은 규칙을 50개씩 끊어 셋씩 병렬로 돌리고, 청크 결과를 코드가 병합한다. 청크끼리 겹치거나 담당 밖 규칙을 건드리는 건 검증기가 막는다. 빠진 규칙은 막지 않는다 — 미커버는 따로 판정하고, S5 가 케이스를 더 만들어 메운다.
형식을 안 정하면 다음 단계가 죽는다
제외 사유를 자유 문장으로 쓰게 뒀더니 에이전트마다 래퍼를 씌우거나 서술형으로 적었고, 다음 단계가 타입 오류로 멈췄다. 명세를 안 쓴 대가다.
지금은 제외 사유가 정확히 세 값 중 하나다. 근거 문장은 따로 담는 자리를 뒀다.
그리고 “추후 구현”은 제외 사유가 될 수 없다. 그건 케이스로 만들어 두고 판정만 유보하는 것이지, 안 만드는 이유가 아니다. 안 만들면 다음 버전에서 아무도 그게 있었다는 걸 모른다.
같은 지적이 몇 번째인지 센다
발견 패턴은 큐에 쌓인다. 그리고 그 큐가 승격 절차를 가진다.
- 한 번 발견되면 관찰 목록에 한 줄
- 같은 유형이 두 번 이상 재발하면 활성 패턴으로 승격
- 석 달마다 활성 패턴을 규칙서에 정식 반영하고 큐에서 뺀다
- 규칙에 반영했는데 또 터지면 재발 감시로 되돌린다
4번이 핵심이다. 규칙을 고쳤는데도 같은 게 나오면 그건 규칙이 안 읽히거나 애매하다는 뜻이고, 다음 조치는 문장을 강화하는 게 아니라 기계 검사로 격상하는 것이다.
지금 큐에는 재발 10회, 13회짜리가 앉아 있다. 그 숫자 자체가 정보다 — 열 번 넘게 같은 지적이 나왔으면 그건 쓰는 사람 문제가 아니라 규칙 문제다.
단, 지금(2026-09 기준) 체인 안의 리뷰는 이 큐를 읽기만 한다. 렌즈는 큐 사본을 받아 자기 영역에서 재발을 점검하지만, 산출 파일 외에는 쓰지 말라는 지시를 받는다. 리뷰 규칙서에는 「끝나면 큐에 적어라」가 남아 있는데, 체인이 그 규칙서를 런타임에 읽지 않는다. 큐에 적는 일은 체인 밖의 수집 명령이 한다 — QA 가 테스트하며 시트를 손으로 고친 흔적을 파이프라인 최종본과 비교해 후보를 내고, 등록은 사람이 명시할 때만 한다.