class MissingRequiredRefError(Exception):
    """A cast subject required by the shot resolved with NO usable hero identity ref."""

    def __init__(self, shot_id: str, subjects: list[str]):
        self.shot_id, self.subjects = shot_id, subjects
        super().__init__(
            f"{shot_id}: required HERO identity refs missing for {subjects!r}. "
            f"Dispatch BLOCKED (fail-closed). Promote a hero to the shelf or "
            f"declare the shot refless."
        )


class MissingBoardRefError(Exception):
    """A board-gated shot reached dispatch with no resolvable board_ref_path."""

    def __init__(self, shot_id: str):
        self.shot_id = shot_id
        super().__init__(f"{shot_id}: board-gated shot has no finished board_ref_path. BLOCKED.")


class SheetIntegrityError(RuntimeError):
    """A composite sheet exists on disk but fails integrity (too small / bad PNG
    magic / sub-512px). Raised to abort LOUDLY before any spend instead of
    silently shipping garbage — the successor to dispatch_payload.CompositeSheetError."""
