# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-refuse-a-delegated-oauth-scope-it-can-technically-satisfy.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-refuse-a-delegated-oauth-scope-it-can-technically-satisfy.warrant.json
# Markdown SHA-256:   dbc5a250deaa9120c23588b5fef616fa5f88a1f985504d5a18a6ff718af23750
# Code AST SHA-256:   77fb34f19c67d08fa5b9ef90f19f364937c524d3367bf909112eed05cb2298d4
# Verified Classes:   AttestationScopeMismatch
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class AttestationScopeMismatch(RuntimeError):
    """Raised when a delegated, runtime-bound scope is not covered by the live attestation."""
    pass


def reconcile_scope(scope: str, attestation: dict, live_digest: str, unbound_allowlist: set[str]) -> None:
    predicate = attestation.get("predicate", {})
    bound = "runtime_digest" in predicate

    if not bound or scope in unbound_allowlist:
        return  # bearer or explicitly unbound scope: no runtime binding claimed

    if scope not in predicate.get("scope_set", set()):
        raise AttestationScopeMismatch(
            f"scope {scope!r} not in attested scope_set {sorted(predicate.get('scope_set', []))}"
        )

    if predicate["runtime_digest"] != live_digest:
        rotation = attestation.get("rotation_hint")
        if rotation and rotation.get("new_digest") == live_digest:
            return  # pending signed rotation covers the live state; re-attest and retry
        raise AttestationScopeMismatch(
            f"live digest {live_digest} does not match attested {predicate['runtime_digest']} "
            f"for bound scope {scope!r}; refusing exchange"
        )
