  # #region ValidationSchemas [C:3] [TYPE Module] [SEMANTICS pydantic, validation, schema, task, run]
  # @defgroup Schemas Module group.
  # @BRIEF Pydantic v2 schemas for validation task management and run history API serialization.
  # @LAYER API
  # @RELATION DEPENDS_ON -> [EXT:Library:pydantic]
  
> from datetime import datetime
> from typing import Any, Literal
  
> from pydantic import BaseModel, ConfigDict, Field
  
  
  # #region SourceInput [C:1] [TYPE Class]
  # @BRIEF Schema for creating a validation source from a dashboard ID or Superset URL.
  # @RATIONALE v2 introduces typed source objects allowing both dashboard_id (existing numeric
  #            reference) and dashboard_url (full Superset link that gets parsed/extracted) inputs.
  # @REJECTED Keeping only dashboard_id rejected — dashboard_url enables sharing Superset links
  #            directly without first resolving numeric IDs manually.
> class SourceInput(BaseModel):
>     type: Literal["dashboard_id", "dashboard_url"] = Field(
>         ..., description="Source type: 'dashboard_id' for numeric IDs, 'dashboard_url' for full Superset URLs"
>     )
>     value: str = Field(..., description="Source value: numeric dashboard ID or full Superset dashboard URL")
  # #endregion SourceInput
  
  
  # #region SourceResponse [C:1] [TYPE Class]
  # @BRIEF Schema for displaying a validation source in API responses.
> class SourceResponse(BaseModel):
>     id: str
>     type: str
>     value: str
>     status: str = "valid"
>     title: str | None = None
>     resolved_dashboard_id: str | None = None
>     last_error: str | None = None
>     last_checked_at: datetime | None = None
>     created_at: datetime | None = None
  
>     model_config = ConfigDict(from_attributes=True)
  # #endregion SourceResponse
  
  
  # #region ValidationTaskCreate [C:2] [TYPE Class]
  # @defgroup Schemas Module group.
  # @BRIEF Schema for creating a new validation task (policy). v2 adds sources, screenshot_enabled,
  #        logs_enabled, execute_chart_data, llm_batch_size, concurrency_limit, and prompt_template.
  # @RATIONALE All v2 fields are optional to support gradual migration. dashboard_ids is kept for
  #            backward compat — when non-empty and sources is empty, auto-create ValidationSource rows.
  # @REJECTED Breaking dashboard_ids removal rejected — existing UI callers and API clients still
  #            send dashboard_ids; removing it would break migration without frontend changes.
> class ValidationTaskCreate(BaseModel):
>     name: str = Field(..., description="Human-readable task name")
>     description: str | None = Field(None, description="Optional task description")
>     environment_id: str = Field(..., description="Target Superset environment ID")
>     dashboard_ids: list[str] = Field(default_factory=list, description="List of dashboard IDs (v1 compat — auto-creates ValidationSource rows if non-empty)")
>     provider_id: str = Field(..., description="LLM provider ID — must be multimodal when screenshot_enabled=True")
>     schedule_days: list[int] | None = Field(None, description="Days of week (0-6, 0=Sunday). Omit or empty for manual-only.")
>     window_start: str | None = Field(None, description="Window start time, HH:MM format")
>     window_end: str | None = Field(None, description="Window end time, HH:MM format")
>     notify_owners: bool = Field(True, description="Notify dashboard owners on failure")
>     custom_channels: list[str] | None = Field(None, description="Additional notification channels")
>     alert_condition: str = Field("FAIL_ONLY", description="FAIL_ONLY, WARN_AND_FAIL, or ALWAYS")
>     is_active: bool = Field(True, description="Whether the task is active")
  
      # v2 fields (all optional for gradual migration)
>     screenshot_enabled: bool = Field(True, description="Enable screenshot-based validation (Path A)")
>     logs_enabled: bool = Field(True, description="Enable log collection during validation")
>     execute_chart_data: bool = Field(False, description="Execute chart data queries during validation")
>     llm_batch_size: int = Field(1, description="Number of dashboards to send in a single LLM batch", ge=1)
>     policy_dashboard_concurrency_limit: int = Field(3, description="Max concurrent dashboard validations for this policy", ge=1)
>     prompt_template: str | None = Field(None, description="Custom prompt template override")
>     sources: list[SourceInput] | None = Field(None, description="v2 source definitions (takes precedence over dashboard_ids)")
  # #endregion ValidationTaskCreate
  
  
  # #region ValidationTaskUpdate [C:2] [TYPE Class]
  # @defgroup Schemas Module group.
  # @BRIEF Schema for updating an existing validation task (policy). Includes v2 fields.
  # @RATIONALE All fields optional (partial update). When sources is provided, replaces all existing
  #            ValidationSource rows for the policy.
> class ValidationTaskUpdate(BaseModel):
>     name: str | None = None
>     description: str | None = None
>     environment_id: str | None = None
>     dashboard_ids: list[str] | None = None
>     provider_id: str | None = None
>     schedule_days: list[int] | None = None
>     window_start: str | None = None
>     window_end: str | None = None
>     notify_owners: bool | None = None
>     custom_channels: list[str] | None = None
>     alert_condition: str | None = None
>     is_active: bool | None = None
  
      # v2 fields
>     screenshot_enabled: bool | None = None
>     logs_enabled: bool | None = None
>     execute_chart_data: bool | None = None
>     llm_batch_size: int | None = None
>     policy_dashboard_concurrency_limit: int | None = None
>     prompt_template: str | None = None
>     sources: list[SourceInput] | None = Field(None, description="Replace all sources when provided")
  # #endregion ValidationTaskUpdate
  
  
  # #region ValidationTaskResponse [C:2] [TYPE Class]
  # @defgroup Schemas Module group.
  # @BRIEF Schema for validation task API responses — includes last run info and v2 fields.
> class ValidationTaskResponse(BaseModel):
>     id: str
>     name: str
>     description: str | None = Field(None, description="Optional task description")
>     environment_id: str
>     is_active: bool
>     dashboard_ids: list[str]
>     provider_id: str | None = None
>     schedule_days: list[int] | None = None
>     window_start: str | None = None
>     window_end: str | None = None
>     notify_owners: bool = True
>     custom_channels: list[str] | None = None
>     alert_condition: str = "FAIL_ONLY"
>     created_at: datetime
>     updated_at: datetime | None = None
>     last_run_status: str | None = None
>     last_run_at: datetime | None = None
>     last_run_summary: dict | None = Field(None, description="Aggregated counts: {pass: N, warn: N, fail: N}")
  
      # v2 fields
>     screenshot_enabled: bool = True
>     logs_enabled: bool = True
>     execute_chart_data: bool = False
>     llm_batch_size: int = 1
>     policy_dashboard_concurrency_limit: int = 3
>     prompt_template: str | None = None
>     sources: list[SourceResponse] = Field(default_factory=list, description="Resolved validation sources")
  
>     model_config = ConfigDict(from_attributes=True)
  # #endregion ValidationTaskResponse
  
  
  # #region ValidationTaskListResponse [C:1] [TYPE Class]
  # @BRIEF Schema for paginated task list response.
> class ValidationTaskListResponse(BaseModel):
>     items: list[ValidationTaskResponse] = []
>     total: int = 0
>     page: int = 1
>     page_size: int = 20
  # #endregion ValidationTaskListResponse
  
  
  # #region TriggerRunResponse [C:1] [TYPE Class]
  # @BRIEF Schema for trigger-run response — returns spawned task ID and run ID.
> class TriggerRunResponse(BaseModel):
>     task_id: str
>     spawned_task_id: str
>     run_id: str | None = Field(None, description="ValidationRun ID (v2)")
>     status: str = "PENDING"
>     message: str = "Validation task spawned"
  # #endregion TriggerRunResponse
  
  
  # #region RunResponse [C:1] [TYPE Class]
  # @BRIEF Schema for v2 ValidationRun display (aggregate run, not individual record).
> class RunResponse(BaseModel):
>     id: str
>     policy_id: str
>     task_id: str | None = None
>     status: str
>     trigger: str = "manual"
>     started_at: datetime | None = None
>     finished_at: datetime | None = None
>     dashboard_count: int = 0
>     pass_count: int = 0
>     warn_count: int = 0
>     fail_count: int = 0
>     unknown_count: int = 0
>     created_at: datetime | None = None
  
>     model_config = ConfigDict(from_attributes=True)
  # #endregion RunResponse
  
  
  # #region RunListResponse [C:1] [TYPE Class]
  # @BRIEF Schema for paginated v2 run list response.
> class RunListResponse(BaseModel):
>     items: list[RunResponse] = []
>     total: int = 0
>     page: int = 1
>     page_size: int = 20
  # #endregion RunListResponse
  
  
  # #region RecordResponse [C:1] [TYPE Class]
  # @BRIEF Schema for a single ValidationRecord in cross-task runs listing.
> class RecordResponse(BaseModel):
>     id: str
>     policy_id: str | None = None
>     run_id: str | None = None
>     dashboard_id: str
>     dashboard_title: str | None = None
>     environment_id: str | None = None
>     status: str
>     summary: str = ""
>     timestamp: datetime | None = None
>     execution_path: str | None = None
>     screenshot_path: str | None = None
>     screenshot_paths: list[str] = Field(default_factory=list)
>     issues: list[dict[str, Any]] = Field(default_factory=list)
>     raw_response: str | None = None
>     dataset_health: dict[str, Any] | None = None
>     chart_data_results: list[dict[str, Any]] = Field(default_factory=list)
>     tab_screenshots: list[dict[str, Any]] = Field(default_factory=list)
>     logs_sent_to_llm: list[str] = Field(default_factory=list)
>     token_usage: dict[str, Any] | None = None
>     timings: dict[str, Any] | None = None
  
>     model_config = ConfigDict(from_attributes=True)
  # #endregion RecordResponse
  
  
  # #region RecordListResponse [C:1] [TYPE Class]
  # @BRIEF Schema for paginated cross-task runs listing.
> class RecordListResponse(BaseModel):
>     items: list[RecordResponse] = []
>     total: int = 0
>     page: int = 1
>     page_size: int = 20
  # #endregion RecordListResponse
  
  
  # #region RunDetailResponse [C:1] [TYPE Class]
  # @BRIEF Schema for v2 run detail — ValidationRun + all its ValidationRecords.
> class RunDetailResponse(BaseModel):
>     run: RunResponse
>     records: list[dict[str, Any]] = Field(default_factory=list)
  # #endregion RunDetailResponse
  
  
  # #endregion ValidationSchemas
