"""Case-verdict read seam — the single place the read path derives pass/connectivity. The engine is the authority: it runs ``judgement.combine_case_outcome`` once and writes each case's verdict into ``summary.case_outcomes``. Every read surface (report generation, the run-logs endpoint) must present *that* verdict, never recompute it — otherwise WEIGHTED/ANY logic and connectivity cases diverge from what was judged. This module is that single seam. It reads the authoritative ``case_outcomes`` when present, and only for older runs that predate it falls back to a documented approximation from persisted turns/results. Pure — no I/O; callers build the per-case ``CaseEvidence`` from whatever they already have in hand. """ from dataclasses import dataclass from agenteval.models import CaseOutcomeSummary @dataclass(frozen=True) class CaseEvidence: """What the legacy approximation needs about one case's persisted record. ``result_passes`` is the per-rule pass flags (empty means no judged rule result exists for the case — the connectivity-vs-fault fork). """ has_turns: bool all_replied: bool result_passes: tuple[bool, ...] = () def resolve_case_verdicts( *, case_outcomes: dict[str, CaseOutcomeSummary], evidence: dict[str, CaseEvidence], errored_case_ids: set[str], ) -> dict[str, CaseOutcomeSummary]: """Resolve every case in ``evidence`` to its authoritative-or-approximated verdict. Authoritative ``case_outcomes`` win verbatim. For a case missing from it (an older run), approximate per CONTEXT.md / ADR-0002: a case with no judged results but turns that all replied and no case-level error is a *connectivity* case (counts as passed); a case with results passes iff every rule passed; anything else (a fault) fails. """ verdicts: dict[str, CaseOutcomeSummary] = {} for case_id, ev in evidence.items(): authoritative = case_outcomes.get(case_id) if authoritative is not None: verdicts[case_id] = authoritative continue connectivity = ( not ev.result_passes and ev.has_turns and ev.all_replied and case_id not in errored_case_ids ) if connectivity: passed = True elif not ev.result_passes: passed = False else: passed = all(ev.result_passes) verdicts[case_id] = CaseOutcomeSummary(passed=passed, connectivity=connectivity) return verdicts