"""Pausing the lottery from code (B-78, DAT-69, AUD-11). A pause only stops the *next* round from opening (rounds/service.py:rounds_can_open); a round in progress still closes, draws and pays. Besides the operator's own switch there are pauses the system raises itself when something needs a human look — a transaction given up for dead that confirmed after all (`resurrected_tx`, B-78) and, in later findings, a deep reorg (`reorg`, B-85) and a payout whose winning bet left the chain (`payout_blocked`, B-82). """ from sqlalchemy.ext.asyncio import AsyncSession from app.audit.log import write_audit_log from app.rounds.config import get_round_config OPERATOR_REASON = "operator" AUTOMATIC_REASONS = ("reorg", "payout_blocked", "resurrected_tx") async def pause_automatically(session: AsyncSession, reason: str) -> None: """Pause the lottery for an automatic `reason` in the caller's transaction and write the `lottery_paused` row. An automatic reason is written even over a pause that is already active (DAT-69): the cause that needs checking must not be hidden behind an earlier one, and an operator pause is never what overrides it.""" if reason not in AUTOMATIC_REASONS: raise ValueError(f"not an automatic pause reason: {reason!r}") config = await get_round_config(session) config.paused = True config.paused_reason = reason await write_audit_log(session, "lottery_paused", {"reason": reason})