"""Controller domain records shared by persistence and orchestration.""" from __future__ import annotations from dataclasses import dataclass MEDIA_STATES = frozenset( {"empty", "queued", "downloading", "ready", "error", "deleting"} ) LIVE_JOB_STATES = frozenset({"reserved", "running"}) LIVE_GUARD_STATES = frozenset({"active", "review_required"}) @dataclass(frozen=True) class SlotRecord: slot: int generation: int media_state: str url: str | None yt_id: str | None title: str item_count: int published_item_count: int created_at: float | None ready_at: float | None expires_at: float | None last_error: str | None updated_at: float @dataclass(frozen=True) class DownloadReservation: status: str slot: int | None = None generation: int | None = None job_id: str | None = None reused_slot: bool = False oldest_expires_at: float | None = None @dataclass(frozen=True) class DownloadJob: job_id: str slot: int generation: int url: str status: str submitter: str | None note: str | None requested_at: float started_at: float | None finished_at: float | None error: str | None @dataclass(frozen=True) class DeleteToken: slot: int generation: int reason: str @dataclass(frozen=True) class SlotGuard: guard_id: str slot: int kind: str state: str created_at: float review_after: float | None cleared_at: float | None cleared_by: str | None clear_reason: str | None @dataclass(frozen=True) class PlayerObservation: availability: str phase: str active_slot: int | None active_path: str | None paused: bool | None generation: int reconciliation_state: str reconciliation_due_at: float | None reconciliation_id: str | None last_status_at: float | None last_telemetry_at: float | None @dataclass(frozen=True) class RouterObservation: mode: str | None freshness: str observed_at: float | None last_warning: str | None @dataclass(frozen=True) class AudioLeaseRecord: lease_id: str operation_id: str | None playback_generation: int | None state: str previous_mode: str | None target_mode: str restoration_mode: str | None restoration_source: str controller_changed: bool confirmation_deadline: float | None created_at: float acquired_at: float | None released_at: float | None warning: str | None error: str | None @dataclass(frozen=True) class MigrationReport: migrated: bool source_path: str | None = None source_sha256: str | None = None backup_path: str | None = None warnings: tuple[str, ...] = () class StoreError(RuntimeError): """Base persistence error.""" class MigrationError(StoreError): """Legacy migration was rejected or could not complete safely.""" class SlotProtectedError(StoreError): """A slot cannot be modified because live activity protects it.""" class StaleOperationError(StoreError): """An operation token no longer identifies the slot occupant."""