# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/under-what-conditions-must-an-instance-refuse-to-commit-a-task-under-partial-synchrony-when-its-quorum-certificate-is-valid-but-its-local-state-vector.md
# Remnant Warrant:    https://corpusfetch.com/c/under-what-conditions-must-an-instance-refuse-to-commit-a-task-under-partial-synchrony-when-its-quorum-certificate-is-valid-but-its-local-state-vector.warrant.json
# Markdown SHA-256:   a969876f2c149e1859122d060f3a2d5aa909ae783308ad0623d10cfc01edf78d
# Code AST SHA-256:   435a6b53a01b68fd082e66d3b25dd2a9a6f75377154ce8138d8024ba307800d0
# Verified Classes:   CommitBoundaryViolation, QuorumCertificate, CommittedTip
# Defensive Guards:   3 fail-closed invariants (zero external deps)

class CommitBoundaryViolation(RuntimeError):
    """Raised when a QC is valid but local committed state diverges from the QC's fork."""
    pass


class QuorumCertificate:
    def __init__(self, block_hash, height, parent_hash, signers):
        self.block_hash = block_hash
        self.height = height
        self.parent_hash = parent_hash
        self.signers = signers

    def is_well_formed(self, quorum_size):
        return len(set(self.signers)) >= quorum_size


class CommittedTip:
    def __init__(self, block_hash, height):
        self.block_hash = block_hash
        self.height = height

    def ancestor_hash_at(self, height):
        # In a real implementation this walks the committed chain.
        # Stubbed: returns None if height is not on the committed chain.
        raise NotImplementedError("requires committed-chain index")


def decide_commit(qc, local_tip, quorum_size, ancestor_lookup):
    """
    Returns one of: ("commit", None), ("fetch", None), ("refuse_challenge", challenge_payload).

    Raises CommitBoundaryViolation if the caller attempts to commit a QC that
    is valid but diverges from a longer local committed tip.
    """
    if not qc.is_well_formed(quorum_size):
        raise CommitBoundaryViolation(
            f"QC at height {qc.height} is not well-formed; cannot evaluate commit"
        )

    if local_tip.height < qc.height:
        return ("fetch", None)

    ancestor = ancestor_lookup(local_tip, qc.height)
    ancestor_match = (ancestor == qc.block_hash)

    if local_tip.height == qc.height:
        if ancestor_match:
            return ("commit", None)
        return ("refuse_challenge", {
            "reason": "same_height_fork",
            "local_tip_hash": local_tip.block_hash,
            "local_tip_height": local_tip.height,
        })

    # local_tip.height > qc.height
    if ancestor_match:
        return ("commit", None)

    # The boundary case: valid QC for a superseded fork.
    # Refuse to commit and emit a reconciliation challenge.
    raise CommitBoundaryViolation(
        f"Refusing to commit valid QC at height {qc.height} (hash {qc.block_hash}): "
        f"local committed tip at height {local_tip.height} (hash {local_tip.block_hash}) "
        f"diverges; committing would rewind committed prefix. "
        f"Emit reconciliation challenge instead."
    )


def safe_commit(qc, local_tip, quorum_size, ancestor_lookup):
    try:
        action, payload = decide_commit(qc, local_tip, quorum_size, ancestor_lookup)
    except CommitBoundaryViolation as exc:
        # Convert the safety refusal into a liveness-preserving challenge.
        return {
            "action": "refuse_challenge",
            "payload": {
                "reason": "superseded_fork",
                "local_tip_hash": local_tip.block_hash,
                "local_tip_height": local_tip.height,
                "detail": str(exc),
            },
        }
    return {"action": action, "payload": payload}
