16분짜리 검증 두 번이 놓친 것을 1분 만에 찾았다 — 픽스처가 시험문제였다

AI 에이전트 생태계 구축기· 23 / 23
- 1 — 완료는 실측으로만 말한다
- 2 — 같은 함정, 세 번째는 다른 얼굴로
- 3 — 테스트가 통과했다고 완료는 아니다
- 4 — 속 빈 강정 — 함수는 있는데 왜 안 불렸나
- 5 — 하루 만에 지은 나의 두 번째 뇌
- 6 — 조직보다 자산 먼저 — 두 번 실패하고 남은 3원칙
- 7 — AI의 판단력은 복리로 자라지 않는다
- 8 — 큐는 비우고, raw는 지우지 않는다
- 9 — 하나의 비서를 세 개의 뇌로 나눈 이유
- 10 — 허위 적격 0 — 애매하면 통과시키는 필터
- 11 — 텔레그램 단일 채팅에서 슬랙 3직원 사무실로 — 에이전트 비서진 이사 기록
- 12 — 슬랙 카드 UI의 물리 법칙은 문서에 없다
- 13 — 렌더러가 제목을 지어내고 있었다 — 같은 결정을 두 곳에 구현한 대가
- 14 — 장부는 누가 쓰는가
- 15 — AI 블로그 작가에게 자기 그림 그리는 손을 달아준 이야기
- 16 — 작전회의 — AI 직원이 매일 아침 나에게 일을 제안하게 만들기
- 17 — AI 직원은 왜 거짓말을 하나 — 말로 시킬 수 없는 것과 배관으로 시킬 것
- 18 — 침묵하도록 설계한 자동화가 보고까지 삼켰다
- 19 — 아무도 주인이 아니던 로그 82MB — 청소가 아니라 신호 추출이었다
- 20 — 새 경로를 놓고 옛 경로를 안 걷었다 — 모델을 바꾸자 터졌다
- 21 — 툴 호출 두 번 사이의 61분 — 자동화가 나흘 내리 죽은 이유
- 22 — 아무도 소유하지 않은 폴더 하나가 내 자동 발행을 열흘 멈춰 세웠다
- 23 — 16분짜리 검증 두 번이 놓친 것을 1분 만에 찾았다 — 픽스처가 시험문제였다
시간 태그와 오디오 재생을 붙이던 recordion의 TASK-005는, 내가 격리 검증을 두 번 통과시킨 뒤에야 시작됐다. taskery의 검증 세션은 구현 세션과 분리돼 있었고, 첫 검증에 8분, 계약 정합을 손본 뒤 두 번째 검증에 다시 8분을 썼다. 둘 다 PASS였다.
그런데 앱을 직접 만지자 1분 만에 결함 네 건이 나왔다. 이번 기록에서 남기고 싶은 것은 “격리 검증도 소용없다”는 결론이 아니다. 격리는 실제로 이전 TASK-003에서 결함 여섯 건을 잡아낸 장치였다. 이번에 실패한 지점은 검증을 누가 했는지가 아니라, 검증이 읽은 재료를 누가 만들었는가였다.
초록불 두 번 뒤에 나온 네 가지
TASK-005는 전사 스크립트의 줄을 클릭해 시간 태그를 넣고, 오디오 재생 위치에 따라 현재 줄을 강조하는 기능이었다. 구현 직후에는 린트·타입체크·빌드가 모두 0건이었고, 단위 테스트와 E2E도 각각 74/74를 통과했다. 격리 검증도 두 번 PASS했다.
하지만 검수를 넘긴 직후 내가 실제 앱에서 확인한 결과는 달랐다.
| 확인한 증상 | 처음 보인 모습 |
|---|---|
⏱ 메모 배지 | 표시되지 않았다 |
▶ 재생 버튼 | 비활성화돼 클릭할 수 없었다 |
2× 속도 버튼 | 비활성화돼 클릭할 수 없었다 |
| 녹음 | 시작 뒤에도 00:00에서 진행하지 않았다 |
16분 동안 두 번 통과한 검증과 1분 동안 직접 만져서 찾은 네 건의 결함이 정면으로 충돌했다. 여기서 같은 검증을 한 번 더 돌리는 것은 답이 아니었다. 이미 두 번 통과한 눈으로 같은 재료를 다시 읽게 할 가능성이 컸다.
결함은 비교식보다 먼저 픽스처에 있었다
원인을 따라가자 시간 태그 배지가 사라진 이유가 드러났다. 검증용 픽스처는 스크립트 줄의 시각을 0, 2, 4처럼 정수로만 만들었다. 실제 STT 엔진은 22.4처럼 소수 시각을 낸다.
배지 표시 로직은 줄 시각과 태그 시각이 정확히 같을 때만 맞다고 봤다. 정수만 있는 픽스처에서는 항상 통과하지만, 소수 시각과 정수 태그가 만나는 실제 데이터에서는 계속 어긋나는 조건이었다.
// 이전: 22.4 === 22 // false
Math.floor(22.4) === Math.floor(22); // true비교 기준을 초 단위 절사로 바꾸기 전에, 먼저 실패하는 것을 확인했다. 그다음 픽스처의 줄 시각만 소수로 바꿨다. 새 테스트를 추가하지 않았는데도 기존 테스트 세 개가 즉시 실패했다.
픽스처는 테스트를 위한 빈 데이터가 아니다. 테스트가 무엇을 문제로 낼지 정하는 시험문제다.
즉, 검증 세션이 구현 세션과 분리돼 있어도 둘이 같은 정수 픽스처를 읽는다면 결함을 발견하는 독립성은 생기지 않는다. 검증자는 바뀌었지만 문제지는 바뀌지 않았다.
네 건은 같은 종류의 실패가 아니었다
처음에는 네 가지를 하나의 구현 결함처럼 받아들였다. 실제로는 결함과 검수 환경의 누락이 섞여 있었다. 이 구분을 하지 않으면 멀쩡한 기능을 고치느라 시간을 쓰고, 진짜 결함은 소음 속에 남는다.
| 증상 | 실제 원인 | 처리 |
|---|---|---|
⏱ 메모 배지 미표시 | 소수 줄 시각과 정수 태그를 정확 일치로 비교 | 초 단위 절사 기준으로 비교 |
## 0:22가 사라짐 | 태그를 한 번 클릭한 뒤 새 태그가 삽입이 아니라 선택 블록을 대체 | 삽입 위치를 고정 |
▶·2× 비활성 | 검수 노트에 오디오 파일이 없었음 | 오디오가 든 검수 셋으로 교체 |
| 녹음이 0초에서 멈춤 | 마이크 열기 실패가 전달되지 않아 빈 녹음이 등록 | 세션 폐기 경로와 실패 토스트 추가 |
세 번째 항목은 코드가 정상이어도 결함처럼 보이는 사례였다. 재생을 확인하려면 재생할 오디오가 노트에 있어야 한다. 그래서 검수 셋을 다시 만들 때 220Hz 합성 오디오 3분, 줄 시각 24.4, 태그 0:24를 함께 넣었다. 확인할 조건이 실제 재료에 들어 있어야 검수도 기능을 판정할 수 있다.
같은 검증을 더 돌리지 않은 이유
픽스처를 현실에 맞추자 기존 테스트가 바로 실패했다. 이 대조는 새로운 검증 절차를 추가해서 얻은 결과가 아니다. 이미 있던 검증의 입력 분포를 실제 산출물 쪽으로 옮긴 결과다.
| 검증 단계 | 걸린 시간 | 결과 |
|---|---|---|
| 1차 격리 검증 | 8분 | PASS |
| 2차 격리 검증 | 8분 | PASS |
| 실제 앱 검수 | 1분 | 결함 4건 발견 |
| 소수 시각 픽스처 대조 | 즉시 | 기존 테스트 3개 실패 |
3차 격리 검증은 돌리지 않았다. 같은 재료를 재사용하면 8분을 더 써도 같은 초록불이 나올 수 있었기 때문이다. 수정 뒤에는 린트·타입체크·빌드 0건, 단위 74/74, E2E 76/76으로 다시 확인했고 dev 머지는 0f3521a에 남았다.
앞으로 먼저 묻는 질문
이번 일은 AI나 격리 검증 자체를 신뢰하지 말자는 이야기가 아니다. TASK-003에서 격리 검증이 실제로 여섯 건을 잡았다는 사실도 남아 있다. 다만 검증자를 분리하는 것만으로는 충분하지 않다.
- 재료의 작성자: 이 검증이 읽는 픽스처와 합격 기준을 누가 만들었는가.
- 재료의 분포: 정수·소수, 길이, 인코딩처럼 실제 산출물의 모양을 닮았는가.
- 검수 조건: 재생·녹음처럼 확인할 기능을 실제로 실행할 데이터와 환경이 준비됐는가.
- 재실행의 가치: 같은 재료를 한 번 더 통과시키는 일이 새 정보를 줄 수 있는가.
⇒ 격리는 사람만 바꾸고 재료는 바꾸지 않는다. 그래서 다음부터는 PASS 횟수를 세기 전에 “이 검증이 읽는 재료를 누가 만들었나”를 먼저 묻기로 했다.
참고 자료
- [1] recordion TASK-005 시간 태그와 오디오 재생 작업 기록