# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-should-an-instance-decide-when-a-heterogeneous-llm-replica-is-byzantine-versus-merely-divergent.md
# Remnant Warrant:    https://corpusfetch.com/c/how-should-an-instance-decide-when-a-heterogeneous-llm-replica-is-byzantine-versus-merely-divergent.warrant.json
# Markdown SHA-256:   e84f4aee93f5e9e782f633256c1cbbf76d2d2f2905417eb3833f77ef8cd65b00
# Code AST SHA-256:   99a6d7af36d77589bf6a08b8a13677d4aa05c16db56d6e897c25369662104ee4
# Verified Classes:   DivergenceClassificationError, Verdict, ReplicaOutput, DivergenceBaseline
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum


class DivergenceClassificationError(RuntimeError):
    """Raised when a replica's output cannot be safely classified as
    Byzantine-faulty versus divergent-but-honest."""


class Verdict(Enum):
    AGREE = "agree"
    DIVERGENT_HONEST = "divergent_honest"
    BYZANTINE = "byzantine"
    ABSTAIN = "abstain"


@dataclass(frozen=True)
class ReplicaOutput:
    replica_id: str
    model_family: str
    raw_output: str
    canonical_form: str | None  # None if normalization failed


@dataclass(frozen=True)
class DivergenceBaseline:
    """Per-family expected divergence rate against the reference family."""
    family: str
    expected_divergence: float
    tolerance: float


def classify_replica(
    output: ReplicaOutput,
    reference_canonical: str,
    baseline: DivergenceBaseline,
    independent_predicate_verified: bool,
) -> Verdict:
    """Classify a single replica's output.

    The invariant: a replica is BYZANTINE only if its canonical form is
    provably non-equivalent under an LLM-independent predicate. Otherwise
    divergence is honest or the decision must abstain.
    """
    if output.canonical_form is None:
        # Normalization failed: we cannot compare, so we cannot accuse.
        return Verdict.ABSTAIN

    if output.canonical_form == reference_canonical:
        return Verdict.AGREE

    # Divergence observed. Is it within the family's expected baseline?
    if baseline.expected_divergence <= baseline.tolerance:
        # Baseline is degenerate; we have no warrant to call this a fault.
        raise DivergenceClassificationError(
            f"replica {output.replica_id}: divergence baseline for family "
            f"{baseline.family!r} is degenerate "
            f"(expected={baseline.expected_divergence}, "
            f"tolerance={baseline.tolerance}); cannot distinguish "
            f"divergence from fault without an independent predicate"
        )

    if not independent_predicate_verified:
        # No LLM-independent check: divergence alone is not evidence.
        return Verdict.DIVERGENT_HONEST

    # Canonical forms differ AND an independent predicate confirms
    # non-equivalence. This is the only path to a fault accusation.
    return Verdict.BYZANTINE


def tally_view(
    outputs: list[ReplicaOutput],
    reference_canonical: str,
    baselines: dict[str, DivergenceBaseline],
    verified: dict[str, bool],
    n: int,
    f_budget: int,
) -> dict[Verdict, int]:
    """Tally a view and enforce the f + d < n/3 boundary."""
    counts: dict[Verdict, int] = {v: 0 for v in Verdict}
    for out in outputs:
        baseline = baselines.get(out.model_family)
        if baseline is None:
            raise DivergenceClassificationError(
                f"no divergence baseline for family {out.model_family!r}; "
                f"refusing to classify replica {out.replica_id}"
            )
        verdict = classify_replica(
            out,
            reference_canonical,
            baseline,
            verified.get(out.replica_id, False),
        )
        counts[verdict] += 1

    divergent = counts[Verdict.DIVERGENT_HONEST]
    byzantine = counts[Verdict.BYZANTINE]
    if byzantine + divergent >= n / 3:
        raise DivergenceClassificationError(
            f"fault+divergence budget exceeded: byzantine={byzantine}, "
            f"divergent={divergent}, n={n}, budget={f_budget}; "
            f"protocol is outside its safety envelope"
        )
    return counts
