# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-delegated-credential-exchange-from-silently-widening-authority-in-aaif-agent-chains.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-delegated-credential-exchange-from-silently-widening-authority-in-aaif-agent-chains.warrant.json
# Markdown SHA-256:   19d88dbac62e55e35cfe5bd055c344ff7056270804997ddcc46c76bdb49b1e35
# Code AST SHA-256:   f4ca2996fa79cf222f6681155b6bc27e206f8d6eff00f9f2301ebe68a29ffeb7
# Verified Classes:   ScopeWideningError
# Defensive Guards:   5 fail-closed invariants (zero external deps)

class ScopeWideningError(RuntimeError):
    """Raised when a delegated credential requests authority outside its parent's scope."""
    pass


def intersect_scopes(requested, parent):
    """Return requested ∩ parent, or raise if requested is not a subset of parent.

    Scopes are exact tokens; wildcards must be pre-expanded by the caller.
    """
    for token in requested:
        if token.endswith(":*") or token == "*":
            raise ScopeWideningError(
                f"non-canonical wildcard scope {token!r} must be expanded before exchange"
            )
    requested_set = set(requested)
    parent_set = set(parent)
    excess = requested_set - parent_set
    if excess:
        raise ScopeWideningError(
            f"requested scopes {sorted(excess)} exceed parent scope {sorted(parent_set)}"
        )
    return sorted(requested_set)


def issue_delegated_credential(requested, parent_credential, max_depth, depth):
    if parent_credential is None:
        raise ScopeWideningError("delegated exchange requires a presented parent credential")
    if depth > max_depth:
        raise ScopeWideningError(
            f"delegation depth {depth} exceeds maximum {max_depth}"
        )
    granted = intersect_scopes(requested, parent_credential["scope"])
    if not granted:
        raise ScopeWideningError("intersection is empty; refusing to issue a no-authority credential")
    return {"scope": granted, "parent": parent_credential["id"], "depth": depth}


if __name__ == "__main__":
    parent = {"id": "cred-hop1", "scope": ["read:docs", "read:index"]}
    try:
        issue_delegated_credential(
            requested=["read:docs", "read:index", "write:cache"],
            parent_credential=parent,
            max_depth=3,
            depth=2,
        )
    except ScopeWideningError as exc:
        print(f"exchange rejected: {exc}")
