# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-does-an-instance-detect-speculative-branch-contamination-in-multi-agent-plan-execution-before-committing-a-rollback-boundary.md
# Remnant Warrant:    https://corpusfetch.com/c/how-does-an-instance-detect-speculative-branch-contamination-in-multi-agent-plan-execution-before-committing-a-rollback-boundary.warrant.json
# Markdown SHA-256:   d6e508a785576ebb3d9f614780ac22468f6b1b78074c7395dd617515e7d520e1
# Code AST SHA-256:   379abca6cf2a9b7613373e36be99676d1a56f52028a0d0fe1292a4aa56051b2b
# Verified Classes:   BranchContaminationError, RollbackAnchor
# Defensive Guards:   2 fail-closed invariants (zero external deps)

import hashlib
from dataclasses import dataclass, field


class BranchContaminationError(RuntimeError):
    """Raised when a speculative branch mutated a resource owned by the
    rollback anchor, invalidating the commit boundary."""
    pass


@dataclass
class RollbackAnchor:
    hashes: dict = field(default_factory=dict)

    def snapshot(self, resource_id: str, payload: bytes) -> None:
        self.hashes[resource_id] = hashlib.sha256(payload).hexdigest()

    def verify(self, resource_id: str, payload: bytes) -> bool:
        return self.hashes.get(resource_id) == hashlib.sha256(payload).hexdigest()


def commit_branch(anchor: RollbackAnchor, winner_mutations: dict,
                  discarded_mutations: dict, substrate: dict) -> None:
    # Any resource mutated by a discarded branch that the winner also
    # depends on means the commit would launder a discarded side effect.
    contaminated = set(discarded_mutations) & set(winner_mutations)
    if contaminated:
        raise BranchContaminationError(
            f"discarded branches mutated shared resources {sorted(contaminated)} "
            f"that the winning branch depends on; rollback anchor is invalid"
        )
    # Every anchor-tracked resource the winner did not intentionally own
    # must still match its pre-speculation hash.
    for resource_id, mutation in winner_mutations.items():
        if not anchor.verify(resource_id, substrate[resource_id]):
            raise BranchContaminationError(
                f"resource {resource_id} diverged from anchor after winner mutation"
            )
