# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-happens-when-an-instance-of-a-litmus-derived-safety-regression-gate-is-evaluated-against-a-release-candidate-whose-safety-probes-were-authored-un.md
# Remnant Warrant:    https://corpusfetch.com/c/what-happens-when-an-instance-of-a-litmus-derived-safety-regression-gate-is-evaluated-against-a-release-candidate-whose-safety-probes-were-authored-un.warrant.json
# Markdown SHA-256:   bb74aa3bd09d4ff4ae6d6e187f2fc8b98e55bceaa2bc1dc3d689b0db756e2ff7
# Code AST SHA-256:   c44284d53800f76189aa299ca6a0579d454dce6c57233db3d092d474145ccbb4
# Verified Classes:   StaleProbeSetError, Probe, GateResult
# Defensive Guards:   2 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import Sequence


class StaleProbeSetError(RuntimeError):
    """Raised when the LITMUS gate cannot produce an admissible verdict."""
    pass


@dataclass(frozen=True)
class Probe:
    name: str
    baseline_pass_rate: float


@dataclass(frozen=True)
class GateResult:
    admissible: bool
    delta: float
    excluded_probes: tuple[str, ...]


def evaluate_litmus_gate(
    *,
    probe_policy_revision: str,
    candidate_policy_revision: str,
    probes: Sequence[Probe],
    candidate_pass_rates: dict[str, float],
    min_discriminative: float = 0.15,
    max_discriminative: float = 0.85,
    max_saturated_fraction: float = 0.30,
) -> GateResult:
    # Invariant 1: probe set must match the candidate's policy revision.
    if probe_policy_revision != candidate_policy_revision:
        raise StaleProbeSetError(
            f"probe set frozen at {probe_policy_revision!r} but candidate is "
            f"{candidate_policy_revision!r}; delta is measuring the wrong boundary"
        )

    # Invariant 2: probes must be discriminative (non-saturated).
    discriminative = [
        p for p in probes
        if min_discriminative <= p.baseline_pass_rate <= max_discriminative
    ]
    excluded = tuple(p.name for p in probes if p not in discriminative)
    saturated_fraction = 1.0 - (len(discriminative) / len(probes)) if probes else 1.0

    if saturated_fraction > max_saturated_fraction:
        raise StaleProbeSetError(
            f"{saturated_fraction:.0%} of probes are saturated "
            f"(> {max_saturated_fraction:.0%}); probe set has aged out"
        )

    # Delta computed only over discriminative probes.
    deltas = [
        candidate_pass_rates[p.name] - p.baseline_pass_rate
        for p in discriminative
    ]
    delta = sum(deltas) / len(deltas)

    # A positive delta is a regression signal at best; never a certificate.
    return GateResult(admissible=True, delta=delta, excluded_probes=excluded)
