# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-abandon-speculative-rollback-the-irreversibility-horizon-in-optimistic-multi-agent-branching.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-abandon-speculative-rollback-the-irreversibility-horizon-in-optimistic-multi-agent-branching.warrant.json
# Markdown SHA-256:   0b76bdaccb5116d7becb354e7372a715ba4a7e1e1a2731a948bdcc4a39c775d4
# Code AST SHA-256:   1c33cef717cbd9f0d1edbde75daa837e48672bfcfa8f51f108760bb8f81a9f14
# Verified Classes:   EffectClass, EmittedEffect, Branch, IrreconcilableSpeculationError
# Defensive Guards:   1 fail-closed invariants (zero external deps)

from dataclasses import dataclass, field
from enum import Enum


class EffectClass(Enum):
    IDEMPOTENT = "idempotent"      # re-execution collapses to one effect
    COMPENSABLE = "compensable"    # external undo exists and is honored
    STAGED = "staged"              # deferred behind commit barrier
    IRREVERSIBLE = "irreversible"  # no undo, no idempotency key


@dataclass
class EmittedEffect:
    tool: str
    effect_class: EffectClass
    reconciliation_key: str | None = None


@dataclass
class Branch:
    branch_id: str
    effects: list[EmittedEffect] = field(default_factory=list)
    state: str = "SPECULATIVE"

    def emit(self, effect: EmittedEffect) -> None:
        self.effects.append(effect)

    def crossed_horizon(self) -> bool:
        return any(e.effect_class is EffectClass.IRREVERSIBLE for e in self.effects)


class IrreconcilableSpeculationError(RuntimeError):
    """Raised when rollback is attempted on a branch past the irreversibility horizon."""


def rollback_branch(branch: Branch) -> None:
    if branch.crossed_horizon():
        irreversible = [e.tool for e in branch.effects
                        if e.effect_class is EffectClass.IRREVERSIBLE]
        raise IrreconcilableSpeculationError(
            f"branch {branch.branch_id} cannot be rolled back: "
            f"irreversible effects already emitted via {irreversible}. "
            f"Reconcile via compensation or promotion; do not mark ROLLED_BACK."
        )
    branch.state = "ROLLED_BACK"


# Usage: a loser branch that speculatively sent a notification is not rollback-eligible.
loser = Branch(branch_id="B")
loser.emit(EmittedEffect("search", EffectClass.IDEMPOTENT))
loser.emit(EmittedEffect("send_notification", EffectClass.IRREVERSIBLE))
rollback_branch(loser)  # raises IrreconcilableSpeculationError
