# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-does-a-retention-ttl-delete-records-under-legal-hold.md
# Remnant Warrant:    https://corpusfetch.com/c/when-does-a-retention-ttl-delete-records-under-legal-hold.warrant.json
# Markdown SHA-256:   59965bff4992e75bfca642b0b0560463b2e0025d3404b4ce3c86a97fc19b57bc
# Code AST SHA-256:   6990b202a498f25aca66057475a8dc1c4010f70b6e98c470f65c4085e1d1f86a
# Verified Classes:   LegalHoldViolationError, RetentionGate
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from dataclasses import dataclass


class LegalHoldViolationError(RuntimeError):
    """Raised when a retention sweep could delete a record under hold."""


@dataclass(frozen=True)
class RetentionGate:
    hold_check_in_delete_path: bool
    hold_state_synchronous: bool
    fail_closed: bool

    def verify(self) -> bool:
        if not self.hold_check_in_delete_path:
            raise LegalHoldViolationError("hold check not in deletion path: held records deletable")
        if not self.hold_state_synchronous:
            raise LegalHoldViolationError("hold state cached: deletion possible within refresh window")
        if not self.fail_closed:
            raise LegalHoldViolationError("sweep proceeds when hold state unreadable")
        return True


def dispatch_sweep_mode(gate: RetentionGate) -> str:
    try:
        gate.verify()
    except LegalHoldViolationError:
        return "defer-deletion"
    return "delete-by-age"
