# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-to-negotiate-capability-scope-during-cross-org-agent-federation-handshakes-before-attestation-chain-closure.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-to-negotiate-capability-scope-during-cross-org-agent-federation-handshakes-before-attestation-chain-closure.warrant.json
# Markdown SHA-256:   fd98411da0a026b76b47a39e2ff152faed7a0b5a7fe32374918676696e2ef01b
# Code AST SHA-256:   4ccb8236158aee8158ef68cc6b84cd4e16a0e8048cf83965968232c2fb8e599b
# Verified Classes:   UnattestedScopeMutationError
# Defensive Guards:   3 fail-closed invariants (zero external deps)

class UnattestedScopeMutationError(RuntimeError):
    """Raised when a scope mutation arrives before the grantor's attestation chain closes."""
    pass


def apply_scope_revision(session, revision):
    # A revision is an authorization event, not protocol noise.
    if revision.signer != session.last_attested_signer:
        raise UnattestedScopeMutationError(
            f"revision signer {revision.signer!r} differs from last attested "
            f"signer {session.last_attested_signer!r}; refusing to mutate scope"
        )

    chain = session.attestation_chain
    if not chain.is_closed():
        raise UnattestedScopeMutationError(
            f"attestation chain for grantor {session.grantor!r} has not closed "
            f"(missing={chain.missing_links()!r}, revoked={chain.revoked_links()!r}); "
            f"scope mutation {revision.mutation!r} refused"
        )

    if session.epoch != chain.epoch:
        raise UnattestedScopeMutationError(
            f"session epoch {session.epoch} advanced past attested epoch "
            f"{chain.epoch}; re-attestation required before mutation"
        )

    if not revision.is_subset_of(session.attested_scope):
        # Expansion or reclassification: must be fully attested, which we just verified.
        session.attested_scope = revision.mutation
    else:
        session.provisional_scope = revision.mutation

    session.last_attested_signer = revision.signer
    return session.attested_scope
