# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-abort-a-bft-quorum-decision-when-adversarial-agent-detection-degrades-the-effective-quorum-below-the-safety-thresho.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-abort-a-bft-quorum-decision-when-adversarial-agent-detection-degrades-the-effective-quorum-below-the-safety-thresho.warrant.json
# Markdown SHA-256:   05067a6cf48a1245c065a21de19819918c6f729b3de3de963db92b1ec3e62059
# Code AST SHA-256:   ff7e1858c7eda193f99809fd5018afd5277abfc19bb349abd427e1ee4ba1672c
# Verified Classes:   QuorumSafetyViolation
# Defensive Guards:   4 fail-closed invariants (zero external deps)

class QuorumSafetyViolation(RuntimeError):
    """Raised when adversarial-agent detection shrinks the effective
    quorum below the safety threshold (n - d < 3f + 1) without a
    reconfiguration certificate that re-derives the fault bound."""
    pass


def validate_quorum_after_detection(
    n: int,
    f: int,
    detected: int,
    reconfig_certificate: bool,
) -> int:
    """
    Validate that removing `detected` agents from an n-member BFT quorum
    preserves safety. Returns the quorum threshold to use for the round.

    Safety invariant: detection reduces n, NOT f. The adversary's undetected
    faults remain, so we must still satisfy (n - detected) >= 3*f + 1
    unless a signed reconfiguration certificate re-derives f'.
    """
    if n < 3 * f + 1:
        raise QuorumSafetyViolation(
            f"Initial membership unsafe: n={n} < 3f+1={3*f+1}"
        )

    residual = n - detected

    if reconfig_certificate:
        # Membership change is authorized: re-derive fault bound.
        f_prime = (residual - 1) // 3
        if residual < 3 * f_prime + 1:
            raise QuorumSafetyViolation(
                f"Reconfiguration cert invalid: residual={residual} "
                f"cannot support f'={f_prime}"
            )
        return 2 * f_prime + 1

    # No certificate: detection must not erode the safety margin.
    if residual < 3 * f + 1:
        raise QuorumSafetyViolation(
            f"Adversarial detection shrank effective quorum: "
            f"n={n}, detected={detected}, residual={residual}, "
            f"required >= {3*f+1} (f={f} unchanged by detection). "
            f"Abort view; demand reconfiguration certificate."
        )

    return 2 * f + 1


def on_detector_event(state: dict, flagged_agents: list) -> int:
    """Called when the adversarial-agent detector fires mid-view."""
    try:
        return validate_quorum_after_detection(
            n=state["n"],
            f=state["f"],
            detected=len(flagged_agents),
            reconfig_certificate=state.get("reconfig_cert", False),
        )
    except QuorumSafetyViolation as e:
        # Halt the view; do not commit under a degraded quorum.
        state["halted"] = True
        state["halt_reason"] = str(e)
        raise
