# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-refuse-an-a2a-agent-card-whose-capabilities-declare-a-task-lifecycle-it-cannot-honor.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-refuse-an-a2a-agent-card-whose-capabilities-declare-a-task-lifecycle-it-cannot-honor.warrant.json
# Markdown SHA-256:   1e29aed9a80892223276926fcd4f06f67d2f5896549bceb5f13f9bbd20ad697a
# Code AST SHA-256:   39649cfde1a587d37c6b29f5414feaad1155c4393ff33d10871763b920d5ee8e
# Verified Classes:   AgentCardLifecycleViolation
# Defensive Guards:   3 fail-closed invariants (zero external deps)

class AgentCardLifecycleViolation(RuntimeError):
    """Raised when a discovered Agent Card's declared capabilities are
    contradicted by the remote agent's observable task lifecycle."""
    pass


def negotiate_task_lifecycle(card: dict, observed_transitions: list[dict]) -> None:
    caps = card.get("capabilities", {})

    declared_history = caps.get("stateTransitionHistory", False)
    declared_streaming = caps.get("streaming", False)

    for evt in observed_transitions:
        state = evt.get("state")
        if state == "completed" and declared_streaming:
            if not evt.get("artifact_streamed", False) and not evt.get("artifacts"):
                raise AgentCardLifecycleViolation(
                    "card declared streaming=true but terminal transition "
                    "emitted no streamed artifact; lifecycle claim unhonorable"
                )
        if state == "input-required" and declared_history:
            history = evt.get("history")
            if history is not None and len(history) == 0:
                raise AgentCardLifecycleViolation(
                    "card declared stateTransitionHistory=true but history "
                    "endpoint returned empty on input-required transition"
                )

    if not declared_streaming:
        working_events = [e for e in observed_transitions if e.get("state") == "working"]
        if working_events and not any(e.get("poll_hint_seconds") for e in working_events):
            raise AgentCardLifecycleViolation(
                "card declared streaming=false with no poll cadence hint; "
                "working state is unobservable within any card-derivable bound"
            )
