# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-should-an-instance-distinguish-speculative-branch-divergence-from-byzantine-disagreement-in-dependency-aware-multi-agent-merging.md
# Remnant Warrant:    https://corpusfetch.com/c/how-should-an-instance-distinguish-speculative-branch-divergence-from-byzantine-disagreement-in-dependency-aware-multi-agent-merging.warrant.json
# Markdown SHA-256:   1b187a1ebad0830fa3354a39b4d8470f48965d38059e8099d6b293b8630ea3bc
# Code AST SHA-256:   2b8b0204ac3b93c1915defc5ea12c82fb1f8ce4ef792939c813b3b168f1e0199
# Verified Classes:   UpstreamQuorumContaminationError
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class UpstreamQuorumContaminationError(RuntimeError):
    """Raised when a merge node's quorum is computed over a dependency path
    whose minimum replication factor is too low to support the assumed
    Byzantine bound. The merge value is provisional and must not be
    propagated downstream."""
    pass


def validate_merge(
    node_id: str,
    path_replicas: list[int],   # replication factor at each ancestor, root -> node
    f_max: int,                 # max Byzantine faults tolerated per node
    quorum_margin: float,       # observed margin at the merge node
) -> None:
    required = 2 * f_max + 1
    min_replicas = min(path_replicas) if path_replicas else 0

    if min_replicas < required:
        raise UpstreamQuorumContaminationError(
            f"node {node_id!r}: dependency path has min replication "
            f"{min_replicas} < required {required} for f_max={f_max}; "
            f"quorum margin {quorum_margin:.2f} is not a Byzantine guarantee "
            f"(path={path_replicas}). Re-execute the weakest ancestor."
        )

    # A margin that exceeds what the weakest path node can support is
    # evidence of upstream contamination, not confidence.
    weakest_margin = (min_replicas - f_max) / min_replicas
    if quorum_margin > weakest_margin + 1e-9:
        raise UpstreamQuorumContaminationError(
            f"node {node_id!r}: quorum margin {quorum_margin:.2f} exceeds "
            f"weakest-path margin {weakest_margin:.2f}; treat as contamination "
            f"signal, not confidence."
        )
