# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/under-what-conditions-must-an-instance-abandon-head-based-trace-sampling-to-preserve-redaction-guarantees.md
# Remnant Warrant:    https://corpusfetch.com/c/under-what-conditions-must-an-instance-abandon-head-based-trace-sampling-to-preserve-redaction-guarantees.warrant.json
# Markdown SHA-256:   e733426eb456710a309ec68b9440212b4e6ed3d4fc9a5c090f5a086877e0a316
# Code AST SHA-256:   aa3f86beaa0c64de55bd17798975ef04750c6bf3386979f2a8a661e582deb61d
# Verified Classes:   RedactionCoverageError
# Defensive Guards:   4 fail-closed invariants (zero external deps)

class RedactionCoverageError(RuntimeError):
    """Raised when a sampling path can retain unredacted span payloads."""
    pass


def assert_sampling_safe(pipeline) -> None:
    """
    Verify that every durable boundary downstream of the sampler
    is preceded by redaction. Raises if any buffer/sink is unguarded.
    """
    if pipeline.redaction_stage != "pre_sample":
        raise RedactionCoverageError(
            f"redaction_stage={pipeline.redaction_stage!r} is downstream of "
            f"sampler; dropped spans may persist unredacted payloads"
        )

    for sink in pipeline.durable_sinks:
        if not sink.redaction_guaranteed:
            raise RedactionCoverageError(
                f"durable sink {sink.name!r} is reachable from the sampler "
                f"without a redaction processor; abandon head-based sampling "
                f"or move redaction upstream of {sink.name!r}"
            )

    if pipeline.tail_buffer is not None and not pipeline.tail_buffer.redacted:
        raise RedactionCoverageError(
            "tail-sampling buffer retains raw attributes; "
            "switch to redact-then-sample before enabling head sampling"
        )


def configure_sampler(pipeline, requested_rate: float) -> str:
    if requested_rate <= 0.0 or requested_rate > 1.0:
        raise RedactionCoverageError(f"invalid sample rate {requested_rate}")

    try:
        assert_sampling_safe(pipeline)
    except RedactionCoverageError:
        # Fall back to redact-then-sample; never silently proceed head-based.
        pipeline.redaction_stage = "pre_sample"
        pipeline.sampler_mode = "redact_then_sample"
        return "redact_then_sample"

    pipeline.sampler_mode = "head_based"
    return "head_based"
