# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/where-must-an-instance-of-a-safety-intervention-hook-be-bound-in-the-otel-genai-span-lifecycle-to-remain-trace-linkable.md
# Remnant Warrant:    https://corpusfetch.com/c/where-must-an-instance-of-a-safety-intervention-hook-be-bound-in-the-otel-genai-span-lifecycle-to-remain-trace-linkable.warrant.json
# Markdown SHA-256:   5261f6821448e406da0e45081cc5ac250db93522f82b928df4853203d05f5eb8
# Code AST SHA-256:   ac96cea7c48c1d0b8289ec173d64a71dfa756ea1bb62d31b792696d492b24224
# Verified Classes:   SafetyHookBindingError
# Defensive Guards:   1 fail-closed invariants (zero external deps)

from opentelemetry import trace

class SafetyHookBindingError(RuntimeError):
    """Raised when a safety intervention hook cannot bind to a live span."""
    pass


def record_safety_intervention(reason: str, policy_id: str) -> None:
    span = trace.get_current_span()
    ctx = span.get_span_context()

    # Condition of validity: span must be valid AND recording.
    if not ctx.is_valid or not span.is_recording():
        raise SafetyHookBindingError(
            f"Safety hook fired outside active span context "
            f"(reason={reason!r}, policy={policy_id!r}, "
            f"valid={ctx.is_valid}, recording={span.is_recording()}). "
            f"Hook must be bound inside start_as_current_span before end()."
        )

    span.add_event(
        "gen_ai.safety.intervention",
        attributes={
            "safety.reason": reason,
            "safety.policy_id": policy_id,
            "safety.trace_id": format(ctx.trace_id, "032x"),
            "safety.span_id": format(ctx.span_id, "016x"),
        },
    )
    span.set_attribute("gen_ai.safety.intervened", True)
