"""calibration 판정 — ruler 가 gold>bad 를 맞히고 단일결함을 표적만(허용 연쇄 관용) 감지하는지. FAIL 이면 judge 는 기본 차단(강제는 --allow-uncalibrated). 절대 rubric 은 여기서만 쓴다.""" PER_COMPARISON_MIN = 2 / 3 AGG_AGREEMENT_MIN = 0.75 AGG_FLIP_MIN = 0.80 GOLD_PREF_MIN = 0.67 def gold_vs_bad_pass(gold_pref, verdict, flip): """gold 에 대한 명확한 선호도와 일관성 검증""" return verdict == "gold" and gold_pref >= GOLD_PREF_MIN and flip >= PER_COMPARISON_MIN def single_defect_pass(target_drop, next_nonallowed_drop, nonallowed_max_drop, pairwise_goldwin, th): """단일 결함이 표적 기준만 충족하는지 검증""" return (target_drop >= th["target-min-drop"] and nonallowed_max_drop <= th["non-target-max-drop"] and (target_drop - next_nonallowed_drop) >= th["target-margin-over-next"] and pairwise_goldwin >= th["pairwise-target-goldwin-min"]) def aggregate_pass(per_comparison, agg_agreement, agg_flip): """집합 수준에서 agreement 와 flip 일관성 검증""" if agg_agreement < AGG_AGREEMENT_MIN or agg_flip < AGG_FLIP_MIN: return False return all(c["agreement"] >= PER_COMPARISON_MIN and c["flip"] >= PER_COMPARISON_MIN for c in per_comparison) def verdict(results): """최종 판정: pass 는 gold-vs-bad AND aggregate AND 모든 single-defects 통과할 때만""" reasons = [] if not results.get("gold-vs-bad"): reasons.append("gold-vs-bad FAIL") if not results.get("aggregate"): reasons.append("aggregate 임계 FAIL") for fid, ok in (results.get("single-defects") or {}).items(): if not ok: reasons.append(f"단일결함 {fid} 격리 FAIL") return {"pass": not reasons, "reasons": reasons}