# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-refuse-to-emit-a-partial-trace-the-privacy-budget-exhaustion-boundary-in-federated-agent-observability.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-refuse-to-emit-a-partial-trace-the-privacy-budget-exhaustion-boundary-in-federated-agent-observability.warrant.json
# Markdown SHA-256:   36dbc0e517a2acf527bee00ea6100316dbeea27d1207a9b46e9b886fd46747e0
# Code AST SHA-256:   2d2de60b96a0e2cab22ef797ff09cea20361fd356d161028aa73d93ee0a0d4a0
# Verified Classes:   ReleaseDecision, DenyRecord, UnattributableHoleError, PrivacyAccountant
# Defensive Guards:   2 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from enum import Enum


class ReleaseDecision(Enum):
    GRANT = "grant"
    DENY = "deny"


@dataclass
class DenyRecord:
    trace_id: str
    span_id: str
    epsilon_remaining: float
    final: bool = True


class UnattributableHoleError(RuntimeError):
    """Raised when a span is denied but the deny record cannot be made durable.

    Emitting the trace in this state would produce a hole indistinguishable
    from a crash, poisoning federation-wide crash detection. Fail closed.
    """


class PrivacyAccountant:
    def __init__(self, budget: float) -> None:
        self.remaining = budget

    def try_release(self, cost: float) -> ReleaseDecision:
        if cost <= self.remaining:
            self.remaining -= cost
            return ReleaseDecision.GRANT
        return ReleaseDecision.DENY


def emit_span(
    accountant: PrivacyAccountant,
    trace_id: str,
    span_id: str,
    cost: float,
    persist_deny,  # callable(DenyRecord) -> bool, returns durability
) -> dict | None:
    """Return the span dict to emit, or None if a durable deny edge was written.

    Raises UnattributableHoleError if denied and the deny edge is not durable.
    """
    decision = accountant.try_release(cost)
    if decision is ReleaseDecision.GRANT:
        return {"trace_id": trace_id, "span_id": span_id, "noisy": True}

    record = DenyRecord(
        trace_id=trace_id,
        span_id=span_id,
        epsilon_remaining=accountant.remaining,
    )
    if not persist_deny(record):
        raise UnattributableHoleError(
            f"denied span {span_id} in trace {trace_id} has no durable deny "
            f"edge; refusing to emit a hole indistinguishable from a crash"
        )
    return None


def emit_trace(
    accountant: PrivacyAccountant,
    trace_id: str,
    spans: list[tuple[str, float]],
    persist_deny,
) -> list[dict]:
    emitted: list[dict] = []
    for span_id, cost in spans:
        try:
            span = emit_span(accountant, trace_id, span_id, cost, persist_deny)
        except UnattributableHoleError:
            # Fail closed: abandon the entire trace rather than emit a
            # partially-attributable graph.
            raise
        if span is not None:
            emitted.append(span)
    return emitted
