"""Base types and enums for all ASF domain models.""" from __future__ import annotations import enum from datetime import datetime, timezone from typing import Any, Annotated from uuid import UUID, uuid4 from pydantic import ( BaseModel, ConfigDict, Field, field_validator, PlainSerializer, ) # --------------------------------------------------------------------------- # Identifiers # --------------------------------------------------------------------------- ASFFieldID = Annotated[ UUID, Field( description="ASF unique identifier (UUID v4)", examples=["3fa85f64-5717-4562-b3fc-2c963f66afa6"], ), ] def generate_id() -> UUID: """Return a new UUID v4 identifier for an ASF entity.""" return uuid4() # --------------------------------------------------------------------------- # Datetime helpers — force UTC on the wire # --------------------------------------------------------------------------- def _utc_serialize(dt: datetime) -> str: if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) return dt.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") UTCDatetime = Annotated[ datetime, PlainSerializer(_utc_serialize, return_type=str, when_used="json"), ] # --------------------------------------------------------------------------- # Base model configuration # --------------------------------------------------------------------------- class ASFBaseModel(BaseModel): """Base Pydantic model used everywhere in ASF.""" model_config = ConfigDict( extra="forbid", populate_by_name=True, use_enum_values=False, from_attributes=True, str_strip_whitespace=True, json_encoders={datetime: _utc_serialize}, ) class TenantScopedModel(ASFBaseModel): """Any record that belongs to a tenant (Organization).""" tenant_id: ASFFieldID = Field( description="Owning tenant (Organization) identifier. " "Always validated against the authenticated context — never " "trust the value supplied by an untrusted client." ) # --------------------------------------------------------------------------- # Artifact type universe (extensible, open) # --------------------------------------------------------------------------- class ArtifactType(str, enum.Enum): """Canonical set of Artifact subtypes in the initial bootstrap. This list is intentionally extensible: adding a new kind of lifecycle entity is a new ArtifactType value, NOT a new MongoDB collection. """ REQUIREMENT = "requirement" EPIC = "epic" STORY = "story" TASK = "task" ARCHITECTURE_DECISION = "architecture_decision" DOCUMENT = "document" SOURCE_FILE = "source_file" COMMIT = "commit" PULL_REQUEST = "pull_request" BUILD = "build" TEST = "test" SECURITY_FINDING = "security_finding" DEPLOYMENT = "deployment" RELEASE = "release" RUNTIME_EVENT = "runtime_event" INCIDENT = "incident" AI_ANALYSIS = "ai_analysis" # --------------------------------------------------------------------------- # Relationship types between artifacts # --------------------------------------------------------------------------- class RelationshipType(str, enum.Enum): """Canonical relationship predicates between two Artifacts. Semantics: ``source target``, e.g. ``REQ-1 implements TASK-7`` means TASK-7 implements REQ-1. For the inverse use IMPLEMENTED_BY. """ IMPLEMENTS = "implements" IMPLEMENTED_BY = "implemented_by" VERIFIES = "verifies" VERIFIED_BY = "verified_by" GENERATED_FROM = "generated_from" DOCUMENTED_BY = "documented_by" DEPENDS_ON = "depends_on" AFFECTS = "affects" DEPLOYED_AS = "deployed_as" CAUSED_BY = "caused_by" SUPERSEDES = "supersedes" RELATED_TO = "related_to" # --------------------------------------------------------------------------- # Job lifecycle # --------------------------------------------------------------------------- class JobStatus(str, enum.Enum): QUEUED = "queued" RUNNING = "running" COMPLETED = "completed" FAILED = "failed" CANCELLED = "cancelled" class JobPriority(str, enum.Enum): LOW = "low" NORMAL = "normal" HIGH = "high" URGENT = "urgent" class JobType(str, enum.Enum): AI_INTERACTIVE = "ai_interactive" AI_BACKGROUND = "ai_background" INDEXING = "indexing" CODE_ANALYSIS = "code_analysis" TESTING = "testing" DEVOPS = "devops" NOTIFICATION = "notification" CUSTOM = "custom" # --------------------------------------------------------------------------- # Audit # --------------------------------------------------------------------------- class AuditAction(str, enum.Enum): CREATED = "created" UPDATED = "updated" DELETED = "deleted" ACCESSED = "accessed" LOGGED_IN = "logged_in" LOGGED_OUT = "logged_out" PERMISSION_CHANGED = "permission_changed" API_KEY_CREATED = "api_key_created" API_KEY_REVOKED = "api_key_revoked"