M5 계획을 세운 지 이틀 만에 코드 슬라이스가 끝났다. 기준선 동결, 검색, 인용, 모델 모드 평가, 실행 흔적. 도구 사용은 뺐다. 이번 PR #28은 코드가 아니라 정리다. 데이터 흐름 한 장, M5 이전과 이후의 정책 기준선 비교, 기록된 실패와 그로 인한 변경의 목록, 그리고 M5가 답하지 못한 질문. 이 글은 그 정리의 요약이다.

무엇이 깨졌나

검색을 붙이기 전에는 모델이 프로필과 공고를 통째로 받고 판정을 냈다. 검색을 붙이자 모델은 기존 입력에 더해 검색된 근거 조각도 받고 그 조각을 인용해야 했고, 인용은 서버가 검증해야 했다. 깨진 것은 대부분 그 경계에서 나왔다. 인용 하나가 읽기 계약의 경계를 넘자 판정 전체가 실패했고, 하네스의 replay는 검색 결과를 모른 채 유효한 인용을 무효로 기록했고, 실제 모델은 인용을 스물두 개 냈지만 그중 여덟 개만 풀렸다. 모델 모드 평가를 붙이자 러너 자체가 깨졌다. 제공자 이름이 무료를 보장하지 않았고, SDK 타임아웃은 본문을 막지 못했고, 골든셋 오류를 알고도 호출했다. 실행 흔적을 붙이자 관측이 결과를 바꿨다.

정리 문서의 실패 카탈로그는 열여덟 줄이다. 사용 확인 1차의 세 가지(지어낸 고용주, 스케일 없는 점수, 폭주한 추론 토큰)부터 마지막 코드 PR 리뷰의 세 가지(흔적 저장 실패가 응답을 실패시킴, 마감 뒤 열린 단계, 저장 스위치가 삭제를 막음)까지, 실패마다 그로 인한 변경이 한 줄씩 붙어 있다. 그 목록에서 세 번 이상 반복된 패턴을 셋 뽑았다.

경계가 실패시킬 범위를 정해야 한다. 점수의 정수 제약을 읽기 계약에 넣었더니 옛 기록을 못 읽었고, 인용 경계를 결과 전체에 적용했더니 항목 하나가 전체를 실패시켰고, 부분적으로만 해석되는 관련성 라벨로 점수를 냈더니 틀린 숫자가 나왔다. 잘못된 인용은 항목만 제외하고, 기존 기록은 읽기 계약과 생성 계약을 나눠 읽을 수 있게 보존하고, 정답이 불완전한 평가는 해당 질의와 전체 집계의 숫자를 내지 않았다. 모두 같은 방식으로 처리한 것은 아니다. 공통점은 오류가 생겼을 때 무엇을 보존하고, 무엇을 제외하며, 어디까지 판단을 중단할지 정한 것이다.

같은 교훈은 새 진입점마다 다시 놓친다. 목적지 변수 거절과 로그·재시도 고정은 라이브 측정 하네스에서 고쳤는데 사용 확인 러너에서 놓쳤다. SDK 타임아웃이 응답 본문을 막지 못한다는 것은 사용 확인 러너의 리뷰에서 확인해 고쳤는데, 모델 모드 러너에서 다시 놓쳤다. analyzer를 만드는 곳이 늘 때마다 같은 점검표가 필요하고, 이제는 그 점검표가 공유 모듈과 리뷰의 재현 절차 둘 다에 있다.

문장이 있다는 것과 그 일이 일어났다는 것은 다르다. 검증 코드가 있어도 실행을 멈추지 않을 수 있고, 후처리가 남기는 고정 문장을 모델도 쓸 수 있고, 관측이 남긴 “성공 0밀리초”는 아직 끝나지 않은 호출일 수 있다. 셋 다 문구나 코드의 존재 대신 실제 실행 여부와 종료 상태를 기록하는 것으로 고쳤다.

정리 문서도 리뷰에 잡혔다

정리 문서는 코드가 아니라서 리뷰가 가벼울 줄 알았다. 반대였다. 리뷰는 네 곳의 과잉 주장을 잡았다.

전송과 승인 범위. “모든 실제 경로는 승인 플래그가 있고 CI에서 거부된다”고 썼는데, ChatGPT 호스트가 부르는 서버 경로에는 CLI 플래그도 CI 차단도 없다. 호스트가 입력과 결과를 먼저 처리하고, OpenRouter는 상위 엔드포인트로 넘긴다. 세 CLI를 한 줄로 묶은 것도 틀렸다. 사용 확인 러너는 전송 승인 하나뿐이고 CI 거부도 무료 모델 강제도 없다. 경로별, 명령별 표로 바꿨다.

보관 범위. “키와 원문은 어디에도 저장하지 않는다”고 썼는데, 키는 내가 환경 파일에 보관하고, 커밋한 정책 기준선에는 합성 프로필·공고·초안의 전문이 들어 있고, 측정 하네스의 inspect 모드는 모델 초안을 파일로 남긴다. 리뷰 문장을 그대로 옮기면 “합성 전문의 커밋 자체가 문제라는 지적이 아니라 현재 개인정보 설명이 잘못됐다는 뜻입니다.” 무엇이 어디에 남고 무엇으로 지우는지 표로 바꿨고, 로컬 삭제가 ChatGPT나 제공자 쪽 보관을 말하지 않는다는 문장을 붙였다.

지표 분모. 미지원 주장률 48/51을 새 인용 사례 일곱 건의 값처럼 썼는데, 리뷰어가 보고서를 사례 id로 합산하니 기존 스물여덟 건이 인용을 하나도 내지 않아 그 마흔세 개 주장이 전부 미지원으로 잡힌 값이었다. 새 사례만의 값은 인용 정확도 4/9다. 분모를 명시했고, 표가 두 데이터셋의 현황이지 향상 비교가 아니라고 적었다.

정책 보장. “근거 없는 주장이 판정이 되는 일은 없다”고 썼는데, 인용 검증기는 무효 인용을 버리고 확신도를 낮출 뿐 판정과 근거와 부족과 결격은 그대로 둔다. 근거 없는 주장은 표시될 뿐, 제거되지는 않는다. 리뷰어는 이렇게 덧붙였다. “위치·문장 일치는 그 문장이 판단을 의미상 뒷받침한다는 보장도 아닙니다.” 정책이 하는 일, 검증기가 하는 일, 실행 실패가 판정이 되지 않는 규칙을 각각의 동사로 나눠 썼다.

네 곳의 공통점은 코드보다 문서가 더 많이 약속했다는 것이다. 문서의 보장도 코드 경로와 실행 결과, 보고서의 분모로 확인해야 했고, 보장을 적을 때는 구현의 동사를 그대로 쓰는 수밖에 없다. 제거한다, 표시한다, 보존한다, 거부한다.

숫자

정책 평가는 M5 이전 28건에서 이후 35건이 됐다. 커버리지와 정책 판정 일치는 둘 다 전부 일치, 필수 결격 재현율은 9/9에서 10/10, PASS가 REALISTIC이 되는 사례는 0으로 유지. 두 기준선 보고서를 다 커밋했고 테스트가 둘 다 현재 러너와 호환되는지 검사한다. 모델 모드는 3건 스모크와 13건 실행이 있지만 둘 다 기준선이 아니다. 하나는 초안 필드가 없던 보고서이고, 하나는 사유 귀속이 문장 일치이던 버전이며, 둘 다 부분 집합이다. 실제 모델을 상대로 한 기준선은 새 키로 골든셋 전체를 돌려야 생긴다.

답하지 못한 질문

사용 확인 1차의 세 번째 질문인 ‘같은 자료를 일반 채팅에 붙였을 때보다 편한가?’에는 그때 “아직 그렇다고 말할 수 없다”고 썼고, 그 뒤로 나란히 재지 않았다. 추가 비용을 들이지 않기로 한 것은 비교 실험의 결론이 아니라, 당시 사용 경험에 따른 내 판단이었다. 저장소가 보여 줄 수 있는 것은 더 좁고 구체적이다. 근거 없는 매칭을 지우는 정책, 그 실행의 입력과 검색 결과에만 해석되는 인용, 결과로 세는 실패, 실행마다 남는 단계 흔적. 보여 줄 수 없는 것은 지원 우선순위를 정하는 사람이 일반 채팅보다 나아지느냐다. 같은 입력을 두 방식으로 돌리고 같은 사람이 읽어야 하는 비교이고, 잡혀 있지 않다.

M5 정리 편은 여기서 마무리한다. 남은 일은 둘이다. 승인된 모델 모드 실행으로 기준선을 만들고, 별도의 사용 확인이나 호스트 판정 호출로 첫 라이브 흔적을 확인하는 것이다. 문서에는 그 자리만 비워 두었다. 데이터 흐름은 ARCHITECTURE.md에, 정리 전문은 MILESTONE_5_WRAP_UP.md에 있다.