"""Tests for harness/judge.py — LLM-as-judge for canary failures. The judge is an advisory triage tool, not part of the hot loop. It classifies canary-mismatch candidates as either "regression" (the formatter made a real CMOS error) or "variant" (the formatter produced a CMOS-legal rendering that happens to differ from the frozen canonical string). These tests use a fake caller — no real API calls. """ import json from harness.judge import Verdict, build_judge_prompt, judge, parse_verdict def test_build_judge_prompt_includes_both_strings_and_type(): prompt = build_judge_prompt( expected="Yu, Charles. *Interior Chinatown*. Pantheon Books, 2020.", candidate="Yu, Charles. *Interior Chinatown*. New York: Pantheon Books, 2020.", cmos_type="book", ) assert "Yu, Charles" in prompt assert "New York" in prompt assert "book" in prompt.lower() assert "cmos" in prompt.lower() or "chicago" in prompt.lower() def test_parse_verdict_extracts_label_and_reasoning(): raw = json.dumps( {"label": "regression", "reasoning": "place of publication added; CMOS 18 drops it."} ) v = parse_verdict(raw) assert v.label == "regression" assert "place of publication" in v.reasoning def test_parse_verdict_accepts_label_only(): raw = json.dumps({"label": "variant", "reasoning": ""}) v = parse_verdict(raw) assert v.label == "variant" assert v.reasoning == "" def test_parse_verdict_normalizes_unexpected_labels_to_unclear(): # A judge that returns something other than regression/variant should # end up labeled 'unclear' so the human reviewer knows to look at it. raw = json.dumps({"label": "maybe", "reasoning": "..."}) v = parse_verdict(raw) assert v.label == "unclear" def test_parse_verdict_handles_code_fences(): # LLM sometimes wraps JSON in ``` fences; parse_verdict should survive. raw = '```json\n{"label": "variant", "reasoning": "abbreviated publisher"}\n```' v = parse_verdict(raw) assert v.label == "variant" assert "abbreviated" in v.reasoning def test_judge_uses_injected_caller(): captured = {} def fake_caller(system: str, user: str) -> str: captured["system"] = system captured["user"] = user return json.dumps( {"label": "regression", "reasoning": "inserted place of publication"} ) v = judge( expected="Yu, Charles. *Interior Chinatown*. Pantheon Books, 2020.", candidate="Yu, Charles. *Interior Chinatown*. New York: Pantheon Books, 2020.", cmos_type="book", caller=fake_caller, ) assert isinstance(v, Verdict) assert v.label == "regression" assert "place of publication" in v.reasoning assert "18" in captured["system"] # CMOS 18 grounding in the system prompt assert "Yu, Charles" in captured["user"]