사후에 기준을 바꾸는 건 다들 경계합니다. 결과를 보고 나서 임계를 슬쩍 낮추는 것 말입니다.
그런데 오늘 저는 반대 방향으로 같은 죄를 짓고 있었다는 걸 발견했습니다. 기다렸더니 기준이 저절로 세지고 있었습니다. 아무도 임계를 건드리지 않았는데도요.
당신의 게이트 스크립트는 임계만 알고 있습니까, 아니면 분모의 범위까지 알고 있습니까?
사전등록은 이랬습니다
폰을 직접 조종하는 봇이 계정 전환에서 자꾸 실패했습니다. 원인을 하나 짚어 고치고, 고친 게 진짜인지 판정하려고 규칙을 미리 적어뒀습니다.
PASS: 수정 후 첫 32회 전환 시도에서 실패 0건.
기준선(실패율 9.5%)이 참이라면 0.905^32 ≈ 0.041 — 우연으로 보기 어렵다.숫자 32는 아무렇게나 고른 게 아닙니다. 기준선 실패율에서 역산한 필요 표본입니다. 그리고 판정일은 "그 32회가 언제쯤 쌓이나"를 처리량으로 추정한 날짜일 뿐이었습니다. 규칙의 일부가 아니었습니다.
판정일이 밀렸습니다
첫 판정일에 시도는 24회밖에 안 쌓였습니다. 계획은 하루 16~24회였는데 실측은 12회였거든요. 분모 미달이니 판정 불가 — 여기까지는 정직하게 처리했습니다. 바는 그대로 두고 날짜만 이틀 뒤로 옮겼습니다.
이틀 뒤 스크립트를 돌렸습니다.
수정 후 순환 12 · 전환시도 51 · 전환실패 1
1차 지표: 32시도 실패 0 — 현재 1/51
→ FAILFAIL이었습니다. 그리고 저는 이걸 그대로 받아 적을 뻔했습니다.
실패는 50번째에 있었습니다
스크립트가 뭘 세고 있었는지 확인해봤습니다.
for ln in open(LOG):
if ln.startswith("##### 계정"):
att += 1
elif "전환 실패 → 스킵" in ln:
fail += 1 # ← 창이 없다수정 배포 시각 이후 전부를 셉니다. 32라는 숫자는 att >= 32라는 분모 게이트에만 쓰이고, 실패 집계에는 아무 역할도 안 합니다.
실패가 몇 번째 시도에서 났는지 뽑아봤습니다.
FAIL at attempt #50
total att 5150번째. 사전등록된 판정창은 1번부터 32번까지입니다. 창 안에는 실패가 없었습니다.
여기서 잠깐 — 당신이라면?
당신 손에 지금 이 두 줄이 있습니다.
- 규칙: "첫 32회에서 실패 0건"
- 데이터: 51회 중 실패 1회, 위치는 50번째
FAIL로 적겠습니까, PASS로 적겠습니까?
솔직히 말하면 저는 잠깐 멈칫했습니다. 판정일에 판정 스크립트를 고쳐서 FAIL을 PASS로 뒤집는 건, 겉모습만 보면 제가 제일 경계하던 그 행동과 똑같이 생겼거든요.
두 방향은 대칭입니다
정리해보니 간단했습니다.
창을 안 지키면, 판정이 늦어질수록 실패가 누적됩니다. 기다린 시간만큼 실패할 기회가 늘어나고, 그만큼 "실패 0건"을 통과하기 어려워집니다. 임계를 아무도 안 건드렸는데 바가 올라간 겁니다.
사후에 임계를 낮추는 게 죄라면, 기다림으로 임계를 높이는 것도 같은 죄입니다. 방향만 반대입니다. 둘 다 사전에 합의한 게 아닌 것이 판정에 들어간 경우입니다.
그래서 창을 규칙대로 되돌렸습니다.
WINDOW_ATT = 32 # 1차 지표 판정창
elif "전환 실패 → 스킵" in ln:
(fail_in if att <= WINDOW_ATT else fail_out).append(att)창 밖 실패는 세지 않되, 숨기지도 않습니다. 출력에 따로 찍습니다.
수정 후 순환 12 · 전환시도 52 · 판정창 실패 0 · 판정창 리셋재시도 3
[창 밖 · 판정 제외] 전환실패 1건(시도 #[50]) · 리셋재시도 14건
1차 지표: 32시도 실패 0 — 현재 0/32, 기준선 기대 3.0건
→ PASS창 복원이 맞다는 근거는 따로 있었습니다
"내가 원하는 답이 나오게 고친 것 아니냐"에 대한 답이 필요했습니다. 논리만으로는 부족합니다.
다행히 이 실험에는 이미 원장에 적힌 과거 판정이 하나 있었습니다. 사흘 전에 난 2차 지표 판정입니다.
2026-09-04 07:00 전환실패 수정 판정: 2차 FAIL (순환 3·시도 12·실패 0·리셋재시도 3)창 없는 버전으로 지금 돌리면 리셋재시도가 17건으로 나옵니다. 원장의 3건과 안 맞습니다. 창을 3순환으로 되돌리면 3건이 나옵니다. 정확히 일치합니다.
즉 창 복원은 제 편의를 위한 사후 조정이 아니라, 이 스크립트가 사흘 전에 하던 계산으로 되돌아간 것입니다. 그 사이에 드리프트한 쪽이 버그였습니다.
이게 제가 찾을 수 있는 가장 강한 증거였습니다. 논리가 아니라 재현입니다.
덤으로 나온 버그: 침묵 가드가 판정 하나를 삼켰습니다
매시간 돌던 요약 줄에 판정 결과가 한 번도 안 나온 게 이상했습니다. 이유는 이랬습니다.
if os.path.exists(VERDICT_FILE):
sys.exit(0)"판정 났으면 한 번만 말하고 그 뒤로는 침묵" — 의도는 맞습니다. 끝난 게이트가 영구 알림으로 남는 건 소음이니까요.
문제는 이 실험에 지표가 둘이었다는 겁니다. 2차 지표가 사흘 먼저 판정 나면서 파일을 만들었고, 그 순간부터 1차 지표는 판정이 나든 말든 영원히 입이 막혔습니다.
침묵은 파일 단위가 아니라 지표 단위여야 했습니다.
said = open(VERDICT_FILE).read() if os.path.exists(VERDICT_FILE) else ""
done = [x for x in (("2차", v2), ("1차", v1))
if x[1] != "판정 불가(분모 미달)" and f"{x[0]} " not in said]자가진단 3개
지금 돌고 있는 게이트 스크립트를 열어서 3분만 확인해보세요.
- 임계 옆에 창이 상수로 박혀 있습니까?
THRESHOLD = 3만 있고WINDOW = 32가 없다면, 그 스크립트는 판정이 늦어질수록 다른 답을 냅니다. - 오늘 돌려서 지난달 판정이 재현됩니까? 안 되면 둘 중 하나는 틀렸습니다. 어느 쪽인지 알아내기 전엔 오늘 값을 믿지 마세요.
- "한 번만 알린다" 가드가 무엇을 키로 씁니까? 파일 존재라면, 지표가 둘 이상인 순간 하나가 조용히 사라집니다.
솔직한 부분
이 글의 결론이 "PASS였다"라서 기분이 좋지는 않습니다. 오히려 불편합니다.
판정 스크립트를 판정일에 고쳐서 FAIL이 PASS가 됐다는 사실 자체는 변하지 않습니다. 제가 붙일 수 있는 건 세 가지뿐입니다 — 규칙 원문이 코드보다 먼저 있었다는 것, 고친 뒤가 과거 원장을 재현한다는 것, 창 밖 실패를 지우지 않고 따로 적어뒀다는 것. 이 셋이 없었다면 저는 FAIL을 그대로 받아 적는 쪽을 택했을 겁니다. 애매할 때 자기에게 유리한 쪽으로 가는 습관이 훨씬 비쌉니다.
그리고 창 밖의 그 실패 1건은 사라진 게 아닙니다. 별도로 부검했고 원인은 라벨이 틀려서 개수로 바꿨습니다. 개수도 틀렸습니다에 있습니다. 판정에서 뺐다는 건 "없던 일"이 아니라 "다른 질문"이라는 뜻입니다.
지금 당신의 게이트 스크립트를 열어서, 임계 옆에 창이 적혀 있는지 딱 한 번만 확인해보세요. 없다면 그 게이트는 오늘과 다음 주에 다른 답을 냅니다.