# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-happens-when-an-instance-speculatively-executes-a-tool-whose-commit-quorum-is-later-invalidated-by-a-byzantine-validator.md
# Remnant Warrant:    https://corpusfetch.com/c/what-happens-when-an-instance-speculatively-executes-a-tool-whose-commit-quorum-is-later-invalidated-by-a-byzantine-validator.warrant.json
# Markdown SHA-256:   b4be3e9a5cb06b412c067c886917f4ad9213a630003cfaba7f5037a4d76c17c7
# Code AST SHA-256:   9fe2aee172df40daff0c82e0396ea49c4a46e156514fe00944a8d45e0b257ab7
# Verified Classes:   SpeculationCommitSkewError, SpeculationLedger
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class SpeculationCommitSkewError(RuntimeError):
    """Raised when a consumed tool result is retroactively invalidated by
    Byzantine commit-quorum rejection after its side effects were emitted."""
    pass


class SpeculationLedger:
    def __init__(self):
        self._entries = {}

    def record(self, tool_id, idempotent, side_effect_emitted):
        self._entries[tool_id] = {
            "idempotent": idempotent,
            "side_effect_emitted": side_effect_emitted,
        }

    def classify_recovery(self, tool_id):
        entry = self._entries.get(tool_id)
        if entry is None:
            raise SpeculationCommitSkewError(
                f"commit rejection for unknown tool {tool_id!r}: "
                "no speculation entry, cannot determine recovery class"
            )
        if not entry["side_effect_emitted"]:
            return "discard_and_rerun"
        if entry["idempotent"]:
            return "replay_with_idempotency_key"
        return "compensate_and_quarantine"


def handle_commit_rejection(ledger, rejected_tool_ids, plan_state):
    for tool_id in rejected_tool_ids:
        recovery = ledger.classify_recovery(tool_id)
        if recovery == "compensate_and_quarantine":
            plan_state.quarantine(tool_id)
            plan_state.emit_compensation(tool_id)
            raise SpeculationCommitSkewError(
                f"non-idempotent tool {tool_id!r} committed a side effect "
                "whose attestation was rejected by quorum; plan quarantined, "
                "compensation emitted, do not retry"
            )
        elif recovery == "replay_with_idempotency_key":
            plan_state.replay(tool_id)
        else:
            plan_state.discard(tool_id)
    return plan_state
