# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-silent-state-corruption-when-speculative-agents-merge-after-a-cost-aware-commit-boundary.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-silent-state-corruption-when-speculative-agents-merge-after-a-cost-aware-commit-boundary.warrant.json
# Markdown SHA-256:   8aab6dd535deed1c1d7a94cc294f0988060b48350fe7670362e671621d53cb4d
# Code AST SHA-256:   fe3e534e16d007a3f09356d143f75d6cd7f5ded25882a3d418ea83839c7c0c33
# Verified Classes:   MergeReconciliationError
# Defensive Guards:   1 fail-closed invariants (zero external deps)

class MergeReconciliationError(RuntimeError):
    """Raised when a speculative merge would leave unowned physical effects."""
    pass


def merge_branches(winner, all_branches, substrate):
    """
    Reconcile a speculative multi-agent merge.

    Invariant: every branch's physical footprint must be either the winner's
    (replayed) or explicitly reconciled (no-op or discarded) before the merge
    is allowed to complete. An empty trace does not imply an empty footprint.
    """
    winner_ns = winner.namespace
    winner_effects = substrate.effects_in(winner_ns)

    unowned = []
    for branch in all_branches:
        if branch is winner:
            continue
        branch_effects = substrate.effects_in(branch.namespace)
        if branch_effects and not branch.reconciliation_declared:
            unowned.append((branch.id, branch_effects))

    if unowned:
        raise MergeReconciliationError(
            "refusing to merge: branches with physical effects but no "
            f"declared reconciliation: {[b for b, _ in unowned]}. "
            "Deleting these effects would itself be a corruption event."
        )

    # Only now is it safe to promote the winner's namespace to canonical.
    substrate.promote(winner_ns)
    for branch in all_branches:
        if branch is not winner:
            substrate.reconcile(branch.namespace, branch.reconciliation_declared)

    return substrate.canonical_state()
