# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-silent-task-state-divergence-when-a2a-v1-0-agent-cards-are-re-signed-mid-negotiation.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-silent-task-state-divergence-when-a2a-v1-0-agent-cards-are-re-signed-mid-negotiation.warrant.json
# Markdown SHA-256:   7bfad51bfda0c732b7352912efe68a4515da23be6a5c659c2c20cb95a341a6fc
# Code AST SHA-256:   d4b194bbfae3d216538cd74f335c2ee89e6fd4b6b727eee5b6cc6fea7598b471
# Verified Classes:   CapabilitySnapshotMismatch, AgentCardSnapshot
# Defensive Guards:   3 fail-closed invariants (zero external deps)

class CapabilitySnapshotMismatch(RuntimeError):
    """Raised when a task transition violates the pinned create-time capability snapshot."""
    pass


class AgentCardSnapshot:
    def __init__(self, key_id: str, capabilities: frozenset, protocol_version: str):
        self.key_id = key_id
        self.capabilities = capabilities
        self.protocol_version = protocol_version


def validate_transition(snapshot: AgentCardSnapshot,
                        live_card: dict,
                        required_capability: str,
                        current_state: str) -> None:
    if snapshot.protocol_version != live_card["protocolVersion"]:
        raise CapabilitySnapshotMismatch(
            f"protocolVersion changed mid-task: pinned={snapshot.protocol_version} "
            f"live={live_card['protocolVersion']}; task in {current_state} must fail explicitly"
        )
    if required_capability not in snapshot.capabilities:
        raise CapabilitySnapshotMismatch(
            f"capability {required_capability!r} absent from pinned snapshot; "
            f"transition illegal at create time"
        )
    if required_capability not in live_card["capabilities"]:
        # Capability withdrawn mid-task: fail explicitly, never hang.
        raise CapabilitySnapshotMismatch(
            f"capability {required_capability!r} withdrawn mid-task "
            f"(pinned keyId={snapshot.key_id}, live keyId={live_card['signature']['keyId']}); "
            f"transition to failed with reason=capability_withdrawn"
        )
    if live_card["signature"]["keyId"] != snapshot.key_id:
        # Key rotated but capability intact: transparent, proceed.
        return
