# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-refuse-an-a2a-task-negotiation-the-capability-scope-divergence-boundary-in-a2a-v1-0.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-refuse-an-a2a-task-negotiation-the-capability-scope-divergence-boundary-in-a2a-v1-0.warrant.json
# Markdown SHA-256:   444bd5d092956308d68087b32bae2c671de63c767f9595f1f3b97d1bf8531269
# Code AST SHA-256:   d8648af230b6cdbbbc8973c01660d9b37dda61a44127f115c308f24b4e7b8d8f
# Verified Classes:   CapabilityScopeDivergence, AgentCardSkill
# Defensive Guards:   1 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import FrozenSet


class CapabilityScopeDivergence(RuntimeError):
    """Raised when a delegated token cannot cover the full lifecycle scope set
    advertised for an A2A skill. Submitting would orphan the task server-side."""


@dataclass(frozen=True)
class AgentCardSkill:
    skill_id: str
    # Scopes the card associates with reaching a TERMINAL state for this skill,
    # across every lifecycle endpoint (send, get, cancel, push-register).
    lifecycle_scopes: FrozenSet[str]


def assert_submittable(skill: AgentCardSkill, token_scopes: FrozenSet[str]) -> None:
    missing = skill.lifecycle_scopes - token_scopes
    if missing:
        raise CapabilityScopeDivergence(
            f"skill={skill.skill_id!r} requires lifecycle scopes {sorted(skill.lifecycle_scopes)}; "
            f"delegated token carries {sorted(token_scopes)}; missing {sorted(missing)}. "
            f"Refusing tasks/send — submission would create an unresolvable obligation."
        )


def negotiate(skill: AgentCardSkill, token_scopes: FrozenSet[str]) -> str:
    assert_submittable(skill, token_scopes)
    return f"tasks/send -> {skill.skill_id}"
