# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-a2a-v1-0-agent-card-discovery-from-silently-binding-to-a-stale-task-lifecycle-state-after-webhook-rebinding.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-a2a-v1-0-agent-card-discovery-from-silently-binding-to-a-stale-task-lifecycle-state-after-webhook-rebinding.warrant.json
# Markdown SHA-256:   1d3175ac71f0f4af58aade43031a8b767b71929cd3fda55a38c071697712eaaf
# Code AST SHA-256:   535b42d144e854d980daf3d8ac6b46f0d33a2350dfa3bf56b1d548bb0aabf29a
# Verified Classes:   BindingCorrelationError, BindingRecord, TaskBindingHistory
# Defensive Guards:   1 fail-closed invariants (zero external deps)

from dataclasses import dataclass, field
from typing import dict


class BindingCorrelationError(RuntimeError):
    """Raised when a webhook delivery cannot be correlated to a trusted
    Agent Card binding for the task it claims to advance."""


@dataclass
class BindingRecord:
    version: str
    endpoint: str
    key_id: str


@dataclass
class TaskBindingHistory:
    # task_id -> ordered list of bindings trusted during that task's life
    history: dict[str, list[BindingRecord]] = field(default_factory=dict)

    def trust(self, task_id: str, binding: BindingRecord) -> None:
        self.history.setdefault(task_id, []).append(binding)

    def accepts(self, task_id: str, binding_version: str, key_id: str) -> bool:
        return any(
            b.version == binding_version and b.key_id == key_id
            for b in self.history.get(task_id, [])
        )


def accept_webhook(
    history: TaskBindingHistory,
    task_id: str,
    binding_version: str,
    key_id: str,
    current_card_binding: BindingRecord,
) -> None:
    if not history.accepts(task_id, binding_version, key_id):
        raise BindingCorrelationError(
            f"task {task_id}: delivery under binding {binding_version}/{key_id} "
            f"is not in trusted history; current card advertises "
            f"{current_card_binding.version}/{current_card_binding.key_id}. "
            f"Refusing to advance task state — task may be stranded in "
            f"input-required on the server."
        )
    # Delivery is correlated; safe to parse payload and advance state.
