# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/under-what-conditions-must-an-instance-abort-a-speculative-branch-before-reconciliation-the-divergent-read-invariant-in-multi-agent-rollback.md
# Remnant Warrant:    https://corpusfetch.com/c/under-what-conditions-must-an-instance-abort-a-speculative-branch-before-reconciliation-the-divergent-read-invariant-in-multi-agent-rollback.warrant.json
# Markdown SHA-256:   f64ea4e4097580282489c1ea87620aafc7b096bf632bd18e00d3818403ef52a8
# Code AST SHA-256:   c7b22e3fd3f985cb821d15f36c9a14cc233edff66085de9844fa63462e6c0170
# Verified Classes:   DivergentReadError, Branch
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from dataclasses import dataclass, field
from typing import Dict, Set, Optional


class DivergentReadError(RuntimeError):
    """Raised when a speculative branch's soft read-set was invalidated by a
    sibling's committed write before reconciliation. Abort before merge."""
    pass


@dataclass
class Branch:
    name: str
    read_versions: Dict[str, int] = field(default_factory=dict)  # key -> version seen
    pinned: Set[str] = field(default_factory=set)                # keys with hard pins
    has_downstream_effects: bool = False


def reconcile(branch: Branch, store_versions: Dict[str, int],
              sibling_write_set: Set[str]) -> None:
    """Gate a speculative branch at reconciliation entry.

    Reconcile only if every intersecting read is version-stable or pinned.
    Otherwise abort before merge; fail closed if side effects already emitted.
    """
    for key, seen_version in branch.read_versions.items():
        if key not in sibling_write_set:
            continue
        if key in branch.pinned:
            if store_versions.get(key) != seen_version:
                raise DivergentReadError(
                    f"branch {branch.name!r}: pinned read of {key!r} "
                    f"expected v{seen_version}, store at v{store_versions.get(key)}"
                )
            continue
        # Soft read intersecting a sibling write: never reconcile.
        if store_versions.get(key) != seen_version:
            if branch.has_downstream_effects:
                raise DivergentReadError(
                    f"branch {branch.name!r}: soft read of {key!r} diverged "
                    f"(v{seen_version} -> v{store_versions.get(key)}) with "
                    f"downstream effects; fail closed, no winner selectable"
                )
            raise DivergentReadError(
                f"branch {branch.name!r}: soft read of {key!r} diverged "
                f"(v{seen_version} -> v{store_versions.get(key)}); abort and respawn"
            )
    # read-set stable against sibling writes: safe to merge
