# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-aaif-agent-authentication-token-revocation-as-a-hard-stop-rather-than-a-soft-signal.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-aaif-agent-authentication-token-revocation-as-a-hard-stop-rather-than-a-soft-signal.warrant.json
# Markdown SHA-256:   3a83f509bc67879a5c8c1eea5fd6d9d3c5c79252eb44f5817e466e8c2d3c8e3b
# Code AST SHA-256:   dcdb0abdf0d3ef9301a9e74a135b51798c2ce83d0bcfb19662f5cc22c4c80103
# Verified Classes:   RevocationHaltError, IndeterminateAuthError
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class RevocationHaltError(RuntimeError):
    """Raised when a delegated mutating agent observes a revoked AAIF token.

    This is terminal. Callers must NOT catch this and retry or refresh.
    """
    pass


class IndeterminateAuthError(RuntimeError):
    """Raised when AAIF introspection is unreachable for a mutating agent.

    Fail-closed: treated the same as revocation for safety purposes.
    """
    pass


def classify_auth_failure(introspection: dict | None, is_mutating: bool) -> str:
    """Classify an AAIF 401 into a handling branch.

    Returns one of: 'halt_revoked', 'halt_indeterminate', 'refresh_expired'.
    Raises for the halt cases so callers cannot accidentally ignore them.
    """
    if introspection is None:
        if is_mutating:
            raise IndeterminateAuthError(
                "AAIF introspection unreachable during mutating operation; "
                "failing closed rather than retrying."
            )
        return "halt_indeterminate"

    if introspection.get("revoked_at") is not None:
        raise RevocationHaltError(
            f"Token revoked at {introspection['revoked_at']} "
            f"(reason={introspection.get('revocation_reason')!r}); "
            "terminal halt, refresh is forbidden."
        )

    # revoked_at is null: this is genuine expiry, refresh is safe.
    return "refresh_expired"


def execute_step(token_introspection: dict | None, is_mutating: bool, action) -> None:
    branch = classify_auth_failure(token_introspection, is_mutating)
    if branch == "refresh_expired":
        # Only reachable when revocation was explicitly ruled out.
        action()
