# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-reject-a-cross-vendor-a2a-agent-card-whose-declared-capabilities-are-structurally-valid-but-semantically-unnegotiab.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-reject-a-cross-vendor-a2a-agent-card-whose-declared-capabilities-are-structurally-valid-but-semantically-unnegotiab.warrant.json
# Markdown SHA-256:   589633ef26bf8271e9cd8e921543b2b3766fa91c86331a1efc2f804b67e21c83
# Code AST SHA-256:   976fe24f2a02836f44a826c893316fd275cacefe59efd5011137e8d34385a63e
# Verified Classes:   UnnegotiableAgentCardError
# Defensive Guards:   3 fail-closed invariants (zero external deps)

class UnnegotiableAgentCardError(RuntimeError):
    """Raised when an Agent Card is structurally valid but cannot be
    negotiated for the intended task class given the client's transport profile."""
    pass


def check_card_negotiable(card: dict, client_profile: dict, task_class: str) -> None:
    caps = card.get("capabilities", {})
    streaming = caps.get("streaming", False)
    push = caps.get("pushNotifications", {}).get("supported", False)

    input_modes = set(card.get("defaultInputModes", []))
    output_modes = set(card.get("defaultOutputModes", []))
    bufferable = set(client_profile.get("bufferable_input_modes", []))
    live_only = set(client_profile.get("live_only_input_modes", []))

    # Invariant 1: live-only input modes on a non-streaming endpoint.
    if task_class == "live-input" and not streaming:
        unservable = (input_modes & live_only) - bufferable
        if unservable:
            raise UnnegotiableAgentCardError(
                f"task_class={task_class!r}: card advertises live-only input modes "
                f"{sorted(unservable)} but capabilities.streaming is False; "
                f"client cannot buffer these modes."
            )

    # Invariant 2: push notifications whose security scheme the client cannot satisfy.
    if task_class == "async-completion" and push:
        schemes = card.get("securitySchemes", {})
        push_scheme = caps.get("pushNotifications", {}).get("securityScheme")
        if push_scheme in schemes and "mtls" in str(schemes[push_scheme]).lower():
            if not client_profile.get("callback_supports_mtls", False):
                raise UnnegotiableAgentCardError(
                    f"task_class={task_class!r}: server push notifications require mTLS "
                    f"under scheme {push_scheme!r}, but client callback listener is "
                    f"plain HTTPS; registration would silently drop callbacks."
                )

    # Invariant 3: output mode only reachable via a disabled channel.
    resub_only = set(card.get("resubscribeOnlyOutputModes", []))
    if task_class == "async-completion" and not streaming:
        unreachable = (output_modes & resub_only)
        if unreachable:
            raise UnnegotiableAgentCardError(
                f"task_class={task_class!r}: output modes {sorted(unreachable)} are "
                f"emitted only on tasks/resubscribe, but streaming is disabled; "
                f"these modes are declared and unreachable."
            )
