#!/usr/bin/env python3 """exp15 — judge-harness SELFTEST on 2-3 pilot traps (checkpoint item d + preliminary FLOOR). Judges A0 vs A0' (SAME chunking = the noise floor: a well-behaved harness should return ~tie, low |paired_diff|, no systematic order bias). Exercises: JSON parse-rate, both-orders, the 6->10 split escalation branch, leave-one-judge-out, catastrophe screen. Cheap (cents). Pilot traps are hand-specified cohesion phenomena located in specific chunks; each is judged on that chunk's A0-final vs A0'-final. Run AFTER anchors_analyze.py (needs *.chunks.json). Run: python judge_selftest.py """ from __future__ import annotations import json import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) import exp15_llm as L from judges import judge_cell AH = Path("/home/ubuntu/books/gu-zhenren/exp15/anchors") TRAP_LEDGER = Path("/home/ubuntu/books/gu-zhenren/exp15/trap_ledger.json") LEDGER = Path("/home/ubuntu/books/gu-zhenren/exp15/judge_selftest_costs.jsonl") OUT = Path("/home/ubuntu/books/gu-zhenren/exp15/judge_selftest.json") A0_TAG = "flat_a0" # blind-greedy anchor (regenerated on flat S2') A0P_TAG = "flat_a0prime" def chunks(tag): return {c["chunk"]: c for c in json.load(open(AH / f"{tag}.chunks.json"))} def locate_chunk(chunk_map, src_probe): """Return the chunk_idx whose source contains src_probe (first ~20 chars).""" probe = src_probe.strip()[:20] for idx, c in chunk_map.items(): if c["source"] and probe in c["source"]: return idx return None def pick_pilots(n=3): """2-3 diverse-type traps from the curated ledger (prefer distinct types crossing an A0 cut).""" led = json.load(open(TRAP_LEDGER))["ledger"] cross = [t for t in led if t.get("crosses_A0_cut")] picks, seen_types = [], set() for t in cross: if t["type"] not in seen_types: picks.append(t); seen_types.add(t["type"]) if len(picks) >= n: break return picks or led[:n] def main(): a0 = chunks(A0_TAG) a0p = chunks(A0P_TAG) pilots = [{"id": t["id"], "type": t["type"], "chunk_idx": locate_chunk(a0, t["anchor_span"]), "src_zone": (t["anchor_span"] + " … " + t["dependent_span"]), "phenomenon": t["phenomenon"], "expected": t["expected_ru"]} for t in pick_pilots(3)] pilots = [p for p in pilots if p["chunk_idx"] is not None] sp = L.Spender(str(LEDGER), per_call_cap=0.05, hard_cap=1.0) results = [] for p in pilots: ck = p["chunk_idx"] a0_final = a0[ck]["final"] armp_final = a0p[ck]["final"] trap = {"id": p["id"], "type": p["type"], "src_zone": p["src_zone"], "phenomenon": p["phenomenon"], "expected": p["expected"]} print(f"\n=== pilot {p['id']} ({p['type']}) chunk {ck} — A0 vs A0' (floor) ===") agg = judge_cell(sp, trap, a0_final, armp_final) results.append(agg) print(json.dumps({k: agg[k] for k in ("n_votes", "parse_fail", "a0_accuracy", "arm_accuracy", "paired_diff_arm_minus_a0", "pairwise", "order_bias", "catastrophe")}, ensure_ascii=False, indent=2)) OUT.write_text(json.dumps({"pilots": results, "total_cost": round(sp.total, 5)}, ensure_ascii=False, indent=2), encoding="utf-8") # selftest checks tot_parse_fail = sum(r["parse_fail"] for r in results) tot_votes = sum(r["n_votes"] for r in results) escalated = any(r["n_votes"] > 2 * 6 for r in results) # >12 -> at least one judge escalated print(f"\n=== SELFTEST SUMMARY ===") print(f"total votes {tot_votes} parse_fail {tot_parse_fail} (want 0) spend ${sp.total:.4f}") print(f"escalation branch exercised (some cell >12 votes): {escalated}") print(f"floor |paired_diff| per pilot: {[abs(r['paired_diff_arm_minus_a0'] or 0) for r in results]} " f"(want small — A0 vs A0' is noise)") print(f"-> {OUT}") if __name__ == "__main__": main()