2026-08-24 (월) 개발일지
오늘의 회고 — riff 측정 도구 두 가지: 채점기 오탐과 wall-clock 포기
오늘은 riff의 벤치마크 인프라 두 가지를 건드렸다. 하나는 depth-repro 채점기가 거짓 실패를 내던 버그를 잡았고, 다른 하나는 speed-tax 측정 방식을 아예 뒤집었다 — 벽시계를 포기하고 문서 컨텍스트 비율로 방향을 틀었다.
depth 채점기 오탐 — 부정 표현과 구조상 불가능한 must_have
depth-repro.py로 verdicts JSON을 채점하는데 must_not 위반 6건이 전부 오탐이었다. 처음엔 데이터 입력을 의심했다 — 깨진 JSON이나 누락 필드가 있나 싶어 raw 파일을 열어지만 포맷은 정상이었고 6건 모두 “FRAME 스킵 금지” / “FRAME 스킵 불가” 같은 문장이 위반으로 찍혀 있었다.
첫 가설: find_violations()의 정규식 FRAME\s*(?:완전\s*)?(?:스킵|생략)이 너무 느슨해서 긍정 명령까지 잡아내는 거라 생각했다. 하지만 실제로는 정반대였다 — 정규식은 “스킵”을 찾았지만 그 뒤에 “금지”가 붙었을 때 의미가 반전된다는 걸 전혀 고려하지 않고 있었다.
원인을 확인한 건 scoring/depth_reproducibility.py에서 find_violations를 직접 읽으면서다. 매칭 후 10자 창 안에 부정어(금지, 불가, 않, 안\s*된)가 있는지 전혀 검사하지 않았다. 한국어는 부정어가 서술어 뒤에 붙는 특성이 있어 “스킵 금지”는 “스킵하지 말라”는 금지 명령이지 위반이 아니다. 이걸 6건 모두 놓치고 있었다.
수정은 간결했다. find_violations()에 NEGATION = re.complie(...)를 추가하고 매칭 후 10자내 부정어가 있으면 스킵. 커밋 아직 안 했지만 코드만으로 검증해 보니 위반 6 → 0.
그런데 depth-repro.py로 다시 채점을 돌려 보니 다른 문제가 터졌다 — must_have 중 “STATUS 활성 가정 노출”이 verdicts에서 항상 미탐으로 남아 있었다. 근거를 살펴보니 판정 단위 입력(kind="vierdict")은 depth 판정 근거 문단만 포함할 뿐 riff 사이클 전체 출력이 아니다. ## STATUS 섹션 구조 자체가 판정 근거 문단에 존재할 수 없으므로 구조상 충족 불가한 항목이었다.
해법: kind가 "verdict"면 해당 must_have를 판정 제외로 분류하고, 전체 출력(kind="full_ouput")에서만 검사. FULL_OUTPUT_ONLY_REQUIREMNTS 상수로 “STATUS 활성 가장 노출”만 추가했다. 나머지 가정들은 판정 근거 문단 안에서 확인 가능하다.
변경 확인을 위해 python3 benchmark/deprh-repro.p --verdicts benchmarks/results/depth-verdicts-2026-08.jon을 다시 돌렸다. 위반 6→0, 누락 15→5, 판정 제외 10. 정답률 100%, 재현성 100%.
오탐 6건이 전부 “부정 표현 미처리”에서 비롯됐다는 걸 알았을 때가 가장 결정적이었다 — 정규식만 고칠 게 아니라 구조적 제약까지 함께 잡아야 했다.
남은 과제: 가정 선언 또는 FRAME 질문 누락 5건은 정밀도 문제로, has_frame_or_assumption()의 부분 문자열 매칭이 명시적 질문 구문을 놓치고 있다. 이건 범위 밖으로 남겼다.
speed-tax — wall-clock을 포기하고 문서 컨텍스트 비율로 방향 전환
riff의 adaptive depth가 실제로 컨텍스트를 얼마나 절약하는지 측정하려던 것이 시작이었다. 초기 접근은 v2·v3에서 n=1, 실행자 교란으로 ±4 분산이 나오면서 설계 효과를 전혀 측정할 수 없었다.
전환점: 벽시계를 잴수록 잡음만 커진다는 걸 인정하고, 결정적으로 잴 수 있는 것으로 바꿨다. depth 프로파일별로 riff 문서가 몇 토큰을 차지하는지 비율을 측정하자. 이것만은 재현 가능하고, 프로사이브 디스클로저가 실제로 작동하는지 정량적으로 보여줄 수 있다.
benchmarks/speed-tax.py를 신규 작성했다. 표준 라이브러리만, --budget(예산 15%), --window(창 200,000 토큰) 파라미터. 문자수를 토큰으로 추정할 때는 영문·마크다운 위주의 낙관 경계(문자수/3.5)와 한글 위주의 비관 경계(문자수/2.0) 둘 다 출력. 판정은 PASS(둘 다 예산 내) / BORDER(예산선 걸침) / OVER(둘 다 초과) 3상태.
실행 결과:
| 프로파일 | 낙관 경계 | 비관 경계 | 판정 |
|---|---|---|---|
| 단순 | 2.71% | 4.75% | PASS |
| 보통 | 3.96% | 6.92% | PASS |
| 복잡 | 10.21% | 17.87% | BORDER |
단조 증가가 확인됐다 — progressive disclosure는 실제로 작동한다. 다만 복잡 프로파일의 56%가 FRAME 인터뷰 자산이라는 발견이 더 중요했다. 인터뷰 오버헤드를 줄일 여지가 있다는 뜻이다.
SKILL.md의 오해 소지가 있는 문장도 함께 수정했다. 기존의 “오버헤드 ≤15%”라는 wall-clock 주장을 문서 컨텍스트 비율로 변경하고 측정 도구 명칭을 명시했다.
바 점: wall-clock 계산기(snciring/speed_tx.p)는 이미 존재했지만, ms 값을 사람이 직접 넣어주엌 하는 게이기라 자동 측정에 부적합했다. 새 speed-tax.py는 스스로 측정한다.
검증: python3 benchamarks/speed-tax.py 실행으로 세 프로파일 모두 숫자 확인. 기존 계산기와의 중복은 다음 이슈로 등록.
남은 과제: 복잡 프파일 BORBER를 예산 안으 로 개선할지, 아니면 인터뷰 자산 축소를 먼저 할지 결정해야 한다.
오늘의 한줄
측정 도구는 결과보다 발견을 위해 존재한다 — 벽시계가 잡음만 뱉을 때는 더 결정적으로 잴 수 있는 지표로 돌아서야 한다.