"""recoil.pipeline.core.exceptions — Typed exceptions for the visual pipeline.

Build A Phase 2 (2026-05-09): introduced MissingAspectRatioError to replace
the silent "9:16" default in modality runners. Per SYNTHESIS lock 11.

gpt-image-2 wire (2026-05-18): introduced DispatchError parent class +
ModelConstraintError for profile-flag-driven capability checks at the
dispatcher boundary. Both classes live here (NOT in dispatch.py) to avoid
a circular import: dispatch.py imports provider adapters via the runner
bootstrap, and providers raise ModelConstraintError — so the exception
must live in a module both can safely import.
"""

from __future__ import annotations

__all__ = [
    "MissingAspectRatioError",
    "DispatchError",
    "ModelConstraintError",
]


class MissingAspectRatioError(ValueError):
    """Raised when a generation payload reaches a runner without aspect_ratio.

    Per Build A Phase 2 (2026-05-09): modality runners no longer accept a
    silent default of "9:16". ``dispatch()`` injects from ``Project`` SSOT when
    ``DispatchContext.project`` is set; if both payload and context are
    absent, this exception fires.

    Recovery: callers must pass aspect_ratio explicitly OR provide a
    ``DispatchContext`` with a valid project name.
    """


class DispatchError(Exception):
    """Base class for dispatcher-boundary failures.

    Raised between model resolution and runner.run() when a payload
    violates a profile-declared constraint, before any provider call
    happens. Subclass for specific failure modes.
    """


class ModelConstraintError(DispatchError):
    """Model-specific capability constraint violated.

    Profile-flag-driven: reads supports_transparent_bg / multi_ref_supported
    from the resolved model profile, raises before runner.run(). Future
    models inherit the check by setting the same flags — no code changes
    needed.
    """
