# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/where-must-an-instance-of-speculative-multi-agent-execution-draw-the-boundary-between-a-speculative-branch-and-an-optimistic-tool-invocation.md
# Remnant Warrant:    https://corpusfetch.com/c/where-must-an-instance-of-speculative-multi-agent-execution-draw-the-boundary-between-a-speculative-branch-and-an-optimistic-tool-invocation.warrant.json
# Markdown SHA-256:   6e03fe2942a4d5623d6547ce2ff572ccdfbba40e0edf152abd0f5d911fe8041c
# Code AST SHA-256:   0eeaddabe3370c98dacd3ac83f8da1abf0524c366da1c42c37586118d4eadb57
# Verified Classes:   UncompensatedCommitmentError, Branch
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class UncompensatedCommitmentError(RuntimeError):
    """Raised when a branch holding an external commitment is rolled back
    as if it were a pure speculative branch. Rollback is not compensation."""
    pass


class Branch:
    def __init__(self, branch_id):
        self.branch_id = branch_id
        self.open_commitments = []  # durable records of non-idempotent tool calls

    def invoke_tool(self, tool, *, idempotent, side_effect_free, compensation=None):
        if not (idempotent and side_effect_free):
            if compensation is None:
                raise UncompensatedCommitmentError(
                    f"branch {self.branch_id}: non-idempotent tool {tool!r} "
                    f"invoked without a registered compensation action"
                )
            # Register the inverse BEFORE the call is issued.
            self.open_commitments.append({"tool": tool, "compensate": compensation})
        return tool()


def rollback(branch: Branch):
    """Discard a losing branch. Refuses if the branch holds commitments."""
    if branch.open_commitments:
        raise UncompensatedCommitmentError(
            f"branch {branch.branch_id} holds {len(branch.open_commitments)} "
            f"open commitment(s); route through compensate(), not rollback()"
        )
    branch.context = None  # safe: no external commitment was ever emitted


def compensate(branch: Branch):
    """Reverse external commitments. May itself fail; caller must retry/escalate."""
    for record in branch.open_commitments:
        record["compensate"]()  # inverse action, not a rollback
    branch.open_commitments.clear()
    branch.context = None
