# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-structural-signals-indicate-scope-authority-divergence-at-the-aaif-to-a2a-to-mcp-identity-seam.md
# Remnant Warrant:    https://corpusfetch.com/c/what-structural-signals-indicate-scope-authority-divergence-at-the-aaif-to-a2a-to-mcp-identity-seam.warrant.json
# Markdown SHA-256:   a7b103b72b8ee5b96b8708e2340c5dd35c8cb3671bba6a0ffa7826969e2c8573
# Code AST SHA-256:   818aa7ea03ba64caeba198e94e5e7d707e3272b3622fb652adf21e6254fed9c9
# Verified Classes:   ScopeAuthorityDivergenceError, AaifCredential, A2aAgentCard, McpServerPolicy
# Defensive Guards:   5 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import FrozenSet, Optional


class ScopeAuthorityDivergenceError(RuntimeError):
    """Raised when AAIF scope authority and A2A/MCP trusted authorities diverge."""
    pass


@dataclass(frozen=True)
class AaifCredential:
    principal: str
    issuer: str
    scope_authority: str
    scopes: FrozenSet[str]
    audience: FrozenSet[str]


@dataclass(frozen=True)
class A2aAgentCard:
    agent_id: str
    authorization_servers: FrozenSet[str]
    re_signs_downstream: bool


@dataclass(frozen=True)
class McpServerPolicy:
    trusted_scope_authorities: FrozenSet[str]
    supported_scopes: FrozenSet[str]
    evaluates_act_claim: bool


def resolve_and_authorize(
    cred: AaifCredential,
    card: A2aAgentCard,
    mcp: McpServerPolicy,
    requested_scope: str,
    mutating: bool,
    act_chain_verified: Optional[bool] = None,
) -> None:
    # Signal 1: authority intersection between AAIF scope authority and A2A card.
    if cred.scope_authority not in card.authorization_servers:
        raise ScopeAuthorityDivergenceError(
            f"AAIF scope_authority={cred.scope_authority!r} not in "
            f"A2A authorization_servers={sorted(card.authorization_servers)!r} "
            f"for agent_id={card.agent_id!r}"
        )

    # Signal 2: scope string present but authority must be trusted by MCP server.
    if requested_scope not in cred.scopes or requested_scope not in mcp.supported_scopes:
        raise ScopeAuthorityDivergenceError(
            f"scope {requested_scope!r} not jointly held by credential and MCP policy"
        )
    if cred.scope_authority not in mcp.trusted_scope_authorities:
        raise ScopeAuthorityDivergenceError(
            f"scope {requested_scope!r} issued by {cred.scope_authority!r} "
            f"not trusted by MCP server authorities="
            f"{sorted(mcp.trusted_scope_authorities)!r}"
        )

    # Signal 3: re-signing hop must carry an evaluated act chain for mutating calls.
    if card.re_signs_downstream and mutating:
        if not mcp.evaluates_act_claim:
            raise ScopeAuthorityDivergenceError(
                f"agent_id={card.agent_id!r} re-signs downstream but MCP server "
                f"does not evaluate act claim; principal {cred.principal!r} is unbound"
            )
        if act_chain_verified is not True:
            raise ScopeAuthorityDivergenceError(
                f"act chain to principal {cred.principal!r} unverified through "
                f"re-signing hop at agent_id={card.agent_id!r}"
            )
