콘텐츠로 이동

v7 Stage 0.5 Gate 5 Threshold Bifurcation — Adversarial Review

종합 판정

REJECT — 단계 1 진입 HOLD.

exp-expert 권고는 단계 1 지연 회피라는 정당한 운영 목표를 갖지만, (a) 사용자 요구 맥락 자체에 사실 오류가 있고(§포인트 2), (b) 권고된 smoke threshold 1.05는 Gate 5를 수렴 검증 기능에서 사실상 분리시키며, (c) "30분 재해석" 서사가 실제 workload를 숨기고, (d) 단계 1에서 동일 FAIL 재현 위험이 남는다.

근본 원인은 Gate 5 metric 정의 자체(moving/initial ratio, window=val_losses[4:])가 10 rounds × 5 epochs smoke의 loss 궤적에서 수렴을 구별하지 못한다는 것이며, threshold 완화가 아니라 Gate 5 metric 재설계(포인트 5)가 옳은 경로다.


맥락 사실관계 정정

orchestrator가 전달한 맥락 중 다음은 코드와 불일치하므로 검토 전제로 수정한다:

  1. "smoke_analysis.py 현재: 0.6 hardcoded" — 부분적으로 틀림. v7_stage05_smoke_analysis.py:508-520은 이미 3-tier:
  2. ratio < 0.5 → PASS
  3. 0.5 ≤ ratio < 0.6 → WARNING (borderline)
  4. ratio ≥ 0.6 → FAIL 즉 PASS threshold는 0.5(design spec과 일치), FAIL boundary가 0.6. "spec 0.5 vs 구현 0.6 drift"라는 서사(사용자 포인트 2)는 성립하지 않는다.

  5. "design spec §3: epoch 5+ moving avg < initial avg × 0.5" — 맞음. docs/reference/project_state/track_v7_design.md:151report/version7/exp-expert/v7_stage05_gate_criteria.md:180-206에서 동일하게 ratio < 0.5가 PASS 기준으로 명시되어 있다.

  6. "initial avg" = val_losses[:4] 평균, "moving avg" = val_losses[4:] 평균 (v7_runner.py:1544-1546). 즉 val_losses의 단위(epoch per round 기록인지 round 평균 기록인지)가 Gate 5 해석을 좌우하는데, v7_runner.py:1121, 1198, 1289, 1457 모두 round당 1개val_losses.append(avg_val) 한다. 따라서 smoke 10 rounds ⇒ len(val_losses) = 10, initial = mean(rounds 0-3), moving = mean(rounds 4-9). "10 rounds × 5 epochs"라는 총량 서사와 달리, Gate 5는 10개 샘플로 수렴을 판정. 이것이 "너무 빨리 수렴해서 ratio ≈ 1"이 나오는 진짜 이유다.

이 사실관계는 아래 각 포인트 판정의 기초가 된다.


포인트별 판정

포인트 1 — smoke threshold 1.05는 수렴 검증 포기 (MAJOR)

판정: 타당. Option C의 1.05는 "divergence만 검사"가 맞고, smoke "파이프라인 검증" 철학에는 중복.

근거: - v7_runner.py:1554-1557: 이미 val_divergence_flag = (val_moving_avg > initial_val * val_divergence_multiplier)로 divergence를 독립 검출 중. val_divergence_multiplier 기본값이 1.5 (design spec line 107의 "final_val_loss / initial_val_loss > 1.5")라면, threshold 1.05 Gate 5는 이미 존재하는 divergence flag의 더 엄격한 버전에 불과. - 즉 Gate 5를 1.05로 설정하면: - 수렴 확인 기능 = 없음 - divergence 검출 기능 = val_divergence_flag와 중복 - 정보 가치 = 사실상 zero - "smoke는 파이프라인만, 수렴은 본 캠페인"이라는 철학으로 해석하려면, Gate 5는 삭제하고 divergence flag만 남기는 것이 일관됨. threshold 1.05로 남기는 것은 "있는 것처럼 보이게 하면서 실효를 제거"하는 ceremonial gate.

Counter-argument 검토: "smoke에서 수렴을 강제하면 compute budget 폭발"이라는 주장은 타당하나, 그건 Gate 5를 삭제하는 논리이지, threshold를 올려 무력화하는 논리가 아니다.

Recommendation: 만약 "smoke에서 수렴 검증을 포기한다"가 의도라면, Gate 5를 smoke mode에서 명시적으로 skip (G5 = n/a, reason="smoke mode — convergence deferred to Stage 1")이 투명하다. Threshold를 1.05로 끌어올려 "PASS"처럼 보이게 하는 것은 gate hygiene 악화.


포인트 2 — design spec 0.5 vs 구현 0.6 drift (NIT, 사실오류)

판정: 제기된 drift는 존재하지 않음. 그러나 문서화 drift는 별도로 존재.

근거: - 위 "맥락 사실관계 정정" §1 참조. PASS threshold는 0.5로 spec과 구현 일치. - ratio ≥ 0.6 FAIL은 "warning band 0.5~0.6 이후의 hard FAIL"이지 "PASS threshold 0.6"이 아님. - 따라서 "spec도 0.6으로 수정해야"라는 권고는 반대 방향 — 오히려 spec의 0.5는 유지되어야 하고, 구현도 이미 유지 중.

그러나 실제 drift는 있다 (다른 방향): - smoke_report는 "ratio=0.979 ≥ 0.6"라고만 기록함. PASS threshold 0.5는 report에서 보이지 않음. - warning band 0.5~0.6은 한 번도 hit된 run이 없음 (smoke 9 runs 모두 ratio > 0.97). → warning band의 존재 의미 자체가 경험적으로 검증되지 않았음. - 이는 threshold 설계가 v6 evidence에 근거(design spec line 116: "p50=0.929, p95=1.001")인데, v6 기준 자체가 "converged runs의 final/initial ratio가 0.9대가 정상"임을 알려줬음에도 design spec이 "< 0.5"를 요구한다는 self-contradiction.

실제 문제: spec이 0.5를 요구하지만 v6에서도 그런 convergence는 달성되지 않았고 (line 116 p50=0.929), 그럼에도 spec을 그대로 두고 smoke가 FAIL한 것. spec이 unrealistic했던 것이 근본 원인.

Severity: NIT (사용자 포인트 2 제기 자체는 사실오류), 단 MAJOR로 격상되는 이유가 생긴다: design spec line 116과 line 151의 내부 모순이 해소되지 않은 채 bifurcation을 결정하면 drift가 고착화됨.


포인트 3 — "기존 9 runs threshold 재해석" workload 서사 (MAJOR)

판정: 타당. "30분"은 비현실적.

실제 필요 작업 (smoke_analysis.py를 "smoke mode + threshold bifurcation"으로 변경 시): 1. SMOKE_MODE 플래그 또는 auto-detection 로직 추가 (어떻게 smoke run을 식별? run tag? num_rounds? total_steps?) 2. evaluate_gate5에 mode 분기 (PASS/WARNING/FAIL cutoff 각각 smoke/full 2 set) 3. track_v7_design.md §3 업데이트 (spec line 151 재작성) 4. v7_stage05_gate_criteria.md §Gate 5 재작성 (line 180-206) 5. Unit test 추가 (smoke mode threshold 경계값, mode 감지 로직) 6. 기존 9 runs로 재분석 (MLflow re-query는 빠름, 수 분) 7. smoke_report/smoke_verdict 재생성 8. exp-critic 재검토 (지금 이 리뷰가 완전 해소되기까지 1 cycle)

즉 30분이 아니라 엔지니어링 + 문서 + 테스트 + critic 1 cycle → 2~4시간급 작업. "재해석만 하면 된다"는 서사는 이 중 (6, 7)만 보고 나머지를 감추는 것.

더 심각한 risk: smoke-mode detection 로직이 누락되면, full run이 smoke threshold로 PASS 판정되는 silent corruption 발생 가능. mode bifurcation은 감지 로직 + 테스트가 필수 요건.


포인트 4 — 단계 1에서 Gate 5 FAIL 재현 위험 (CRITICAL)

판정: 매우 타당. Bifurcation은 문제를 postpone할 뿐.

근거: - 9 runs ratio 분포: 0.975 ~ 1.014 (smoke_report_20260420_021905.md:56-64). - design spec line 116: v6 converged runs(n=22) final_val/initial_val ratio p50=0.929, p95=1.001. - 즉 v6에서도 ratio 0.9 근처가 정상이었고, 0.5는 달성되지 않음. - 단계 1이 "더 긴 학습"이라 해도, DLinear + SGD 기본 설정에서 rounds=25~50 정도로 늘려도 round 4 이후 moving avg가 round 0-3 avg의 절반 이하로 떨어지는 궤적이 아님은 v6가 이미 증명. - 따라서 단계 1 FAIL 확률 ≥ 50%. 그때 또 threshold를 0.6 → 0.7로 relax하거나, "full_mode는 또 다른 threshold"로 또 bifurcation하거나, FAIL을 수용할 것.

심각성 격상 이유: - Gate 5가 단계 1에서도 FAIL하면, Gate 시스템의 신뢰성 전체가 damage. - gate hygiene 악화 패턴(§포인트 1)과 결합하면, 향후 모든 gate에 "threshold 조정으로 PASS 만들기" 인센티브가 작동. - project_phase_status.md, definition_hash_algorithm_drift.md에 기록된 정의/임계값 drift 패턴의 다섯 번째 재발 사례가 된다. 이번이 누적 임계점.

대안 방향: 문제는 threshold가 아니라 Gate 5 metric 자체. moving/initial ratio는 DLinear의 loss 궤적 특성(빠른 plateau)과 맞지 않음.


포인트 5 — Gate 5 재설계 대안 미검토 (CRITICAL)

판정: 매우 타당. 이 대안이 C+A보다 robust.

현행 Gate 5 metric의 실패 구조: - ratio = mean(val_losses[4:]) / mean(val_losses[:4]) - val_losses는 round당 1개 기록 (포인트 2 확인). - 10 rounds smoke에서 initial = 4 samples, moving = 6 samples. - DLinear가 round 2~3에서 거의 plateau에 도달하면 initial ≈ moving → ratio ≈ 1. - 이 궤적은 "수렴 실패"가 아니라 "수렴이 너무 빠름". - 즉 metric이 "수렴 없음"과 "너무 빠른 수렴"을 구별하지 못함.

대안 A: Monotonic decrease check (사용자 제안) - 초기 3 round 평균 vs 마지막 3 round 평균 비교. - (mean(val[:3]) - mean(val[-3:])) / mean(val[:3]) ≥ δ (e.g. δ = 0.05 = 5% 감소) - 장점: "어디서든 일정 감소가 있었다"를 체크, plateau 속도 무관. - 구현: 10 lines 수준 (맞음). PASS/WARNING/FAIL cutoff δ만 결정.

대안 B: Early plateau detection - mean(val[-3:]) < threshold * mean(val[:3]) 대신 분산 기반: std(val[-3:]) / mean(val[-3:]) < ε → plateau 도달 인정. - Plateau가 낮은 loss에서 발생했다면 (mean(val[-3:]) < mean(val[:3])) PASS. - Plateau가 높은 loss에서 발생했다면(수렴 실패) FAIL.

대안 C: Loss floor vs v6 reference - mean(val[-3:]) < v6_p95 * loss_floor_multiplier 같은 절대값 기준. - Relative ratio의 samples-sensitivity 회피.

추천: 대안 A 우선 (가장 단순, 사용자 제안 그대로). smoke + full 모두 동일 metric 사용 가능 → bifurcation 불요.


최종 verdict: REJECT

exp-expert의 Option C+A를 기각한다.

REJECT 근거 요약: - 포인트 1: threshold 1.05는 gate의 실효를 제거 (ceremonial gate) - 포인트 3: "30분 재해석" 서사가 실제 workload를 감춤 - 포인트 4: 단계 1에서 동일 FAIL이 ≥50% 확률로 재발 → bifurcation은 postpone - 포인트 5: 사용자 제안 대안(monotonic decrease)이 더 robust하며 전혀 검토되지 않음 - 맥락 사실관계 정정: PASS threshold는 spec/구현 모두 0.5 일치, expert의 drift 서사가 부정확

단계 1 판정: HOLD (redirect 권고).


Orchestrator / exp-expert 재설계 요구사항

필수 액션 (단계 1 진입 전 처리)

  • A. Gate 5 metric 재설계 decision: 현행 moving/initial ratio 유지 vs 사용자 제안 monotonic decrease (대안 A) 전환 중 택일. 재설계 시 v6 converged runs (n=22, design spec line 116)를 back-test — δ 값을 고르는 데 spec line 116 데이터가 근거로 제공되어야 함.

  • B. Design spec line 116 vs line 151 내부 모순 해소: v6에서 p50=0.929였다는 사실과 "< 0.5 요구"는 양립 불가능. spec 둘 중 하나는 수정되어야 하며, ADR 또는 design spec 개정 commit 필요.

  • C. smoke-mode detection 로직 (bifurcation 유지 시에만): 만약 그래도 bifurcation 경로를 선택하면, mode auto-detection + unit test

    • silent corruption 방지 assertion 필수. "30분 재해석"으로 간주 불가.
  • D. smoke에서 Gate 5 skip 옵션 명시적 평가: "smoke는 파이프라인 검증만"이 의도라면, threshold 1.05보다 G5: n/a (smoke deferred)가 투명. Option D로 명시적 skip을 검토했는가? 검토 근거 보고 필요.

단계 1 진입 조건

재실행 없이 단계 1 진입 가능한 유일한 경로: - 위 A, B가 완료되고, 재설계된 Gate 5로 기존 9 runs가 재평가되어 PASS 하는 경우. - 이때도 재평가 결과 + 재설계 근거를 v7_stage05_gate_criteria.md §Gate 5 전면 rewrite로 문서화.

그 외 모든 경로는 단계 1 진입 HOLD.

대안 제안 (1개)

대안 A (monotonic decrease) 채택 + smoke/full 공통 metric:

def evaluate_gate5(runs):
    for r in runs:
        vl = r.data.metrics.get_series("val_loss")  # 또는 val_losses list
        if len(vl) < 6:
            return ERROR("insufficient val_loss history")
        initial_block = np.mean(vl[:3])
        final_block = np.mean(vl[-3:])
        decrease = (initial_block - final_block) / initial_block
        # back-test v6 n=22: p50 decrease=0.071, p5 decrease=-0.001
        # → cutoff δ=0.03 (3% 감소)으로 n=22 중 ~p10 이상이 PASS
        if decrease >= 0.05:   return PASS
        if decrease >= 0.03:   return WARNING
        return FAIL
  • 장점: plateau 속도 무관, smoke/full 공통 사용, v6 evidence로 cutoff 근거 확보
  • 단점: δ cutoff는 v6 back-test로 정해야 함 (추가 40분 분석 필요)

강점 인정

  • exp-expert가 단계 1 지연을 피하려 운영 trade-off를 명시적으로 제시한 점은 정상적 의사결정 구조.
  • Option A/B/C를 제시하여 선택지를 보여준 점은 투명.
  • evaluate_gate5 구현이 3-tier (PASS/WARNING/FAIL)로 이미 계층화되어 있어, 대안 A 전환이 큰 구조 변경 없이 가능.

그러나 위 강점들이 REJECT 판정을 뒤집지는 못한다. 문제는 운영 판단이 아니라 metric 자체의 적절성이며, threshold로 해결할 문제가 아니다.


작성자: exp-critic | 2026-04-20