# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-refuse-a-federated-agent-manifest-the-transitive-key-binding-gap-in-signed-agent-credentials.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-refuse-a-federated-agent-manifest-the-transitive-key-binding-gap-in-signed-agent-credentials.warrant.json
# Markdown SHA-256:   3bf0558bb7889d4a8d91461e06b742b86eb3dc258d216a0dfae83593ea1afcb5
# Code AST SHA-256:   7f67c5723b5ecbd50cf84ed1404f97a91652b0eee25bab401f36c06367a26c91
# Verified Classes:   DelegationChainError, Capability, Credential
# Defensive Guards:   5 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import Optional


class DelegationChainError(RuntimeError):
    """Raised when a federated agent manifest chain violates a delegation invariant."""
    pass


@dataclass(frozen=True)
class Capability:
    resource: str
    action: str  # "invoke" or "delegate"


@dataclass(frozen=True)
class Credential:
    subject: str
    issuer: str
    capabilities: frozenset
    delegation_depth_remaining: Optional[int]
    revoked: bool = False


def verify_delegation_edge(parent: Credential, child: Credential, resource: str) -> None:
    if parent.revoked or child.revoked:
        raise DelegationChainError(
            f"revoked credential in chain: parent_revoked={parent.revoked} "
            f"child_revoked={child.revoked}"
        )

    if parent.delegation_depth_remaining is None:
        raise DelegationChainError(
            f"parent {parent.subject} omitted delegation_depth_remaining; "
            "cannot infer authority from absence"
        )

    if parent.delegation_depth_remaining <= 0:
        raise DelegationChainError(
            f"parent {parent.subject} has exhausted delegation depth "
            f"({parent.delegation_depth_remaining})"
        )

    delegate_cap = Capability(resource=resource, action="delegate")
    if delegate_cap not in parent.capabilities:
        # The non-obvious failure: invoke:<resource> is NOT sufficient to delegate.
        raise DelegationChainError(
            f"parent {parent.subject} holds no delegate capability for {resource!r}; "
            f"string-subset scope does not imply delegation authority"
        )


def verify_chain(chain: list, resource: str) -> None:
    if len(chain) < 2:
        raise DelegationChainError("chain must contain at least one delegation edge")
    for parent, child in zip(chain, chain[1:]):
        verify_delegation_edge(parent, child, resource)


if __name__ == "__main__":
    root = Credential(
        subject="did:org-a:planner",
        issuer="did:org-a:root",
        capabilities=frozenset({Capability("tool:payments", "invoke")}),
        delegation_depth_remaining=2,
    )
    leaf = Credential(
        subject="did:org-b:executor",
        issuer="did:org-a:planner",
        capabilities=frozenset({Capability("tool:payments", "invoke")}),
        delegation_depth_remaining=0,
    )
    try:
        verify_chain([root, leaf], resource="tool:payments")
    except DelegationChainError as exc:
        print(f"refused: {exc}")
