import uuid from datetime import datetime from typing import Any, Literal from pydantic import BaseModel, ConfigDict, Field class SourceRef(BaseModel): repo: str = Field(min_length=1, max_length=100) path: str = Field(min_length=1, max_length=1000) revision: str = Field(pattern=r"^[0-9a-f]{40,64}$") class ReviewContractProject(BaseModel): contract: dict[str, Any] source: SourceRef decision_id: uuid.UUID | None = None workplan_id: uuid.UUID | None = None task_id: uuid.UUID | None = None required_for_decision: bool = False class ReviewContractRead(BaseModel): model_config = ConfigDict(from_attributes=True) id: uuid.UUID contract_key: str schema_version: str contract_digest: str source_repo: str source_path: str source_revision: str document: dict[str, Any] active: bool required_for_decision: bool decision_id: uuid.UUID | None workplan_id: uuid.UUID | None task_id: uuid.UUID | None projected_at: datetime created_at: datetime updated_at: datetime class ReviewReceiptSubmit(BaseModel): owner_id: str = Field(min_length=1, max_length=160) actor: str = Field(min_length=2, max_length=160) disposition: Literal["approve", "request_changes"] contract_digest: str = Field(pattern=r"^[0-9a-f]{64}$") artifact_hashes: dict[str, str] = Field(default_factory=dict) assertion_ids: list[str] = Field(default_factory=list) checks: list[dict[str, Any]] = Field(default_factory=list) note: str | None = Field(default=None, max_length=2000) source: SourceRef class ReviewReceiptRead(BaseModel): model_config = ConfigDict(from_attributes=True) id: uuid.UUID contract_id: uuid.UUID owner_id: str actor: str disposition: str contract_digest: str receipt_digest: str artifact_hashes: dict[str, str] assertion_ids: list[str] checks: list[dict[str, Any]] note: str | None source_repo: str source_path: str source_revision: str submitted_at: datetime created_at: datetime class OwnerReviewState(BaseModel): owner_id: str status: Literal["missing", "approved", "request_changes", "stale"] receipt_id: uuid.UUID | None = None receipt_digest: str | None = None submitted_at: datetime | None = None class GateReviewState(BaseModel): gate_id: str policy: Literal["all_required"] owners: list[str] satisfied: bool class ReviewAggregateRead(BaseModel): contract_key: str contract_digest: str satisfied: bool authorizes_execution: Literal[False] = False owners: list[OwnerReviewState] gates: list[GateReviewState]