generator client {
  provider = "prisma-client-js"
  output   = "../src/generated/prisma"
}

datasource db {
  provider = "postgresql"
}

// ─── Enums ───────────────────────────────────────────────

enum ComplexityClass {
  SIMPLE
  MEDIUM
  COMPLEX
  EXPERT
}

enum RoutingMode {
  AUTO
  MANUAL_MODEL
  LOCAL_ONLY
  PRIVACY_FIRST
  LOW_LATENCY
  HIGH_REASONING
  COST_SAVER
}

enum OutcomeLabel {
  CORRECT_IMPROVEMENT
  BAD_REGRESSION
  COST_WIN
  QUALITY_WIN
  UNCERTAIN
}

enum FeedbackValue {
  POSITIVE
  NEGATIVE
}

enum RoutingExecutionStatus {
  SUCCEEDED
  FAILED
  DEGRADED
  NO_EXECUTION_MODEL
}

enum JudgeOutcome {
  VERIFIED
  REVISED
  ESCALATED
  UNAVAILABLE
  NONE
}

// ─── Smart Router Flagship — Phase 1: Model Intelligence Registry ───

enum ModelLifecycle {
  ACTIVE
  PREVIEW
  DEPRECATED
  DISABLED
  REMOVED
}

enum CostConfidence {
  EXACT
  ESTIMATED
  UNKNOWN
}

enum CostClass {
  FREE
  CHEAP
  STANDARD
  PREMIUM
  ULTRA
}

enum LatencyClass {
  REALTIME
  FAST
  MEDIUM
  SLOW
  HEAVY
}

enum QualityTier {
  S
  A
  B
  C
  D
}

enum PrivacyClass {
  PUBLIC_OK
  CLOUD_PERMITTED
  LOCAL_PREFERRED
  LOCAL_ONLY
}

enum ModalityKind {
  TEXT
  CODE
  IMAGE_INPUT
  IMAGE_OUTPUT
  AUDIO_INPUT
  AUDIO_OUTPUT
  VIDEO_INPUT
  VIDEO_OUTPUT
  FILE_INPUT
  PDF_INPUT
  SPREADSHEET_INPUT
  WEB_INPUT
  YOUTUBE_INPUT
  STRUCTURED_OUTPUT
  TOOL_CALLING
  STREAMING
  EMBEDDING
}

enum WorkflowKind {
  DIRECT_LLM
  SEARCH_FIRST
  EXTRACT_FIRST
  PDF_EXTRACTION
  YOUTUBE_TRANSCRIPT
  IMAGE_ANALYSIS
  IMAGE_GENERATION
  VIDEO_ANALYSIS
  AUDIO_TRANSCRIBE
  FILE_GENERATION
  CODE_REVIEW
  COMPARE_ENSEMBLE
  JUDGE_PIPELINE
}

enum DomainTag {
  GENERAL
  CODING
  MEDICAL
  LEGAL
  FINANCE
  MARKETING
  EDUCATION
  CREATIVE_WRITING
  RESEARCH
  MECHANICAL
  AUTOMOTIVE
  BIOLOGY
  CHEMISTRY
  PHYSICS
  MULTIMEDIA
  BUSINESS
  HR
  SALES
  TRANSLATION
  MENTAL_HEALTH
  LITERATURE
}

// ─── Models ──────────────────────────────────────────────

model RoutingDecision {
  id                      String                  @id @default(cuid())
  messageId               String?                 @map("message_id")
  threadId                String                  @map("thread_id")
  messageContent          String?                 @map("message_content")
  selectedProvider        String                  @map("selected_provider")
  selectedModel           String                  @map("selected_model")
  routingMode             RoutingMode             @map("routing_mode")
  confidence              Decimal?                @db.Decimal(5, 4)
  reasonTags              String[]                @map("reason_tags")
  privacyClass            String?                 @map("privacy_class")
  costClass               String?                 @map("cost_class")
  fallbackProvider        String?                 @map("fallback_provider")
  fallbackModel           String?                 @map("fallback_model")
  complexityClass         ComplexityClass?        @map("complexity_class")
  detectedCategory        String?                 @map("detected_category")
  secondaryCategory       String?                 @map("secondary_category")
  matchCount              Int?                    @map("match_count")
  selectedExecutionPath   String?                 @map("selected_execution_path")
  routeRoadmap            Json?                   @map("route_roadmap")
  modelInventorySnapshot  Json?                   @map("model_inventory_snapshot")
  connectorHealthSnapshot Json?                   @map("connector_health_snapshot")
  capabilityMatchScore    Decimal?                @map("capability_match_score") @db.Decimal(5, 4)
  latencyScore            Decimal?                @map("latency_score") @db.Decimal(5, 4)
  riskScore               Decimal?                @map("risk_score") @db.Decimal(5, 4)
  uncertaintyScore        Decimal?                @map("uncertainty_score") @db.Decimal(5, 4)
  explanation             Json?
  routingDurationMs       Int?                    @map("routing_duration_ms")
  // Semantic Router Flagship Phase 2 — shadow-mode analyzer output.
  // Populated async by SemanticIntentAnalyzerManager when
  // ROUTING_SEMANTIC_ANALYZER_ENABLED=true. Phase 4 promotes this to
  // an input for the AI Route Planner; until then it's observe-only.
  semanticAnalysis        Json?                   @map("semantic_analysis")
  // Semantic Router Flagship Phase 4 — shadow-mode planner output. Chained
  // after semanticAnalysis (uses it as input). Stored AIRoutePlanRecord
  // including status, plan, validationIssues, attempts, durationMs.
  aiRoutePlan             Json?                   @map("ai_route_plan")
  // Semantic Router Flagship Phase 6 — live workflow wiring. Optional so
  // the v1 hot path keeps writing rows when the wiring is disabled.
  // Today only DIRECT_LLM + SEARCH_FIRST are LIVE; the rest are surfaced
  // through `selectedWorkflow=NULL` plus the workflowReason string.
  selectedWorkflow        WorkflowKind?           @map("selected_workflow")
  workflowReason          String?                 @map("workflow_reason")
  createdAt               DateTime                @default(now()) @map("created_at")
  outcomes                RoutingOutcomeRecord[]
  feedbackRecords         RoutingFeedbackRecord[]

  @@index([messageId])
  @@index([threadId])
  @@index([routingMode])
  @@index([selectedProvider, selectedModel])
  @@index([detectedCategory])
  @@index([createdAt])
  @@index([selectedWorkflow])
  @@map("routing_decisions")
}

model RoutingPolicy {
  id          String      @id @default(cuid())
  name        String
  routingMode RoutingMode @map("routing_mode")
  priority    Int         @default(0)
  isActive    Boolean     @default(true) @map("is_active")
  config      Json
  createdAt   DateTime    @default(now()) @map("created_at")
  updatedAt   DateTime    @updatedAt @map("updated_at")

  @@index([routingMode])
  @@index([isActive])
  @@index([priority])
  @@map("routing_policies")
}

model ReplayRun {
  id              String       @id @default(cuid())
  name            String?
  filters         Json
  totalReplayed   Int          @map("total_replayed")
  changedCount    Int          @map("changed_count")
  suspiciousCount Int          @map("suspicious_count")
  avgConfOld      Decimal      @map("avg_conf_old") @db.Decimal(5, 4)
  avgConfNew      Decimal      @map("avg_conf_new") @db.Decimal(5, 4)
  avgImprovement  Decimal      @map("avg_improvement") @db.Decimal(6, 4)
  labelBreakdown  Json         @map("label_breakdown")
  createdAt       DateTime     @default(now()) @map("created_at")
  cases           ReplayCase[]

  @@index([createdAt])
  @@map("replay_runs")
}

model ReplayCase {
  id                    String       @id @default(cuid())
  runId                 String       @map("run_id")
  decisionId            String?      @map("decision_id")
  messagePreview        String       @map("message_preview")
  messageContent        String?      @map("message_content")
  hasOriginalContent    Boolean      @map("has_original_content")
  oldProvider           String       @map("old_provider")
  oldModel              String       @map("old_model")
  oldConfidence         Decimal?     @map("old_confidence") @db.Decimal(5, 4)
  oldCostClass          String?      @map("old_cost_class")
  newProvider           String       @map("new_provider")
  newModel              String       @map("new_model")
  newConfidence         Decimal      @map("new_confidence") @db.Decimal(5, 4)
  newCostClass          String       @map("new_cost_class")
  changed               Boolean
  improvementScore      Decimal      @map("improvement_score") @db.Decimal(6, 4)
  outcomeLabel          OutcomeLabel @map("outcome_label")
  isSuspicious          Boolean      @map("is_suspicious")
  suspiciousReasons     String[]     @map("suspicious_reasons")
  isConfirmedRegression Boolean      @default(false) @map("is_confirmed_regression")
  reviewNotes           String?      @map("review_notes")
  isPromoted            Boolean      @default(false) @map("is_promoted")
  reviewedAt            DateTime?    @map("reviewed_at")
  run                   ReplayRun    @relation(fields: [runId], references: [id], onDelete: Cascade)
  createdAt             DateTime     @default(now()) @map("created_at")

  @@index([runId])
  @@index([isSuspicious])
  @@index([outcomeLabel])
  @@index([isConfirmedRegression])
  @@map("replay_cases")
}

model RoutingOutcomeRecord {
  id                     String                 @id @default(cuid())
  routingDecisionId      String                 @map("routing_decision_id")
  messageId              String?                @map("message_id")
  assistantMessageId     String?                @map("assistant_message_id")
  threadId               String                 @map("thread_id")
  finalExecutionProvider String                 @map("final_execution_provider")
  finalExecutionModel    String                 @map("final_execution_model")
  executionStatus        RoutingExecutionStatus @map("execution_status")
  executionSuccess       Boolean                @default(true) @map("execution_success")
  actualLatencyMs        Int?                   @map("actual_latency_ms")
  actualCostEstimate     Decimal?               @map("actual_cost_estimate") @db.Decimal(12, 8)
  finalStatus            String                 @default("completed") @map("final_status")
  fallbackUsed           Boolean                @default(false) @map("fallback_used")
  judgeOutcome           JudgeOutcome           @default(NONE) @map("judge_outcome")
  judgeConfidence        Decimal?               @map("judge_confidence") @db.Decimal(5, 4)
  criticScore            Decimal?               @map("critic_score") @db.Decimal(5, 4)
  issueTags              String[]               @default([]) @map("issue_tags")
  revised                Boolean                @default(false)
  escalated              Boolean                @default(false)
  followUpSignal         String?                @map("follow_up_signal")
  createdAt              DateTime               @default(now()) @map("created_at")
  updatedAt              DateTime               @updatedAt @map("updated_at")
  routingDecision        RoutingDecision        @relation(fields: [routingDecisionId], references: [id], onDelete: Cascade)

  @@unique([routingDecisionId])
  @@index([threadId])
  @@index([executionStatus])
  @@index([judgeOutcome])
  @@map("routing_outcome_records")
}

model RoutingFeedbackRecord {
  id                 String           @id @default(cuid())
  routingDecisionId  String?          @map("routing_decision_id")
  messageId          String           @map("message_id")
  threadId           String           @map("thread_id")
  assistantMessageId String?          @map("assistant_message_id")
  feedbackValue      FeedbackValue    @map("feedback_value")
  source             String           @default("thumbs")
  weight             Decimal          @default(1.0) @db.Decimal(6, 4)
  taskFamily         String?          @map("task_family")
  createdAt          DateTime         @default(now()) @map("created_at")
  routingDecision    RoutingDecision? @relation(fields: [routingDecisionId], references: [id], onDelete: SetNull)

  @@index([routingDecisionId])
  @@index([threadId])
  @@index([feedbackValue])
  @@map("routing_feedback_records")
}

model RouterModelProfile {
  id                           String   @id @default(cuid())
  provider                     String
  model                        String
  taskFamily                   String   @map("task_family")
  topicKey                     String   @map("topic_key")
  routeCount                   Int      @default(0) @map("route_count")
  successRate                  Decimal  @default(0) @map("success_rate") @db.Decimal(6, 4)
  thumbsUpRate                 Decimal  @default(0) @map("thumbs_up_rate") @db.Decimal(6, 4)
  thumbsDownRate               Decimal  @default(0) @map("thumbs_down_rate") @db.Decimal(6, 4)
  judgeVerifiedRate            Decimal  @default(0) @map("judge_verified_rate") @db.Decimal(6, 4)
  judgeRevisedRate             Decimal  @default(0) @map("judge_revised_rate") @db.Decimal(6, 4)
  judgeEscalatedRate           Decimal  @default(0) @map("judge_escalated_rate") @db.Decimal(6, 4)
  averageLatencyMs             Int      @default(0) @map("average_latency_ms")
  averageCostEstimate          Decimal  @default(0) @map("average_cost_estimate") @db.Decimal(12, 8)
  fallbackSuccessRate          Decimal  @default(0) @map("fallback_success_rate") @db.Decimal(6, 4)
  hallucinationRiskScore       Decimal  @default(0) @map("hallucination_risk_score") @db.Decimal(6, 4)
  calibrationTrustScore        Decimal  @default(0) @map("calibration_trust_score") @db.Decimal(6, 4)
  weightedSuccessScore         Decimal  @default(0) @map("weighted_success_score") @db.Decimal(6, 4)
  weightedDissatisfactionScore Decimal  @default(0) @map("weighted_dissatisfaction_score") @db.Decimal(6, 4)
  sampleSize                   Int      @default(0) @map("sample_size")
  confidenceInProfile          Decimal  @default(0) @map("confidence_in_profile") @db.Decimal(6, 4)
  lastUpdated                  DateTime @default(now()) @map("last_updated")

  @@unique([provider, model, taskFamily, topicKey])
  @@index([taskFamily, topicKey])
  @@index([weightedSuccessScore])
  @@map("router_model_profiles")
}

model RouterTopicProfile {
  id                   String   @id @default(cuid())
  taskFamily           String   @map("task_family")
  topicKey             String   @map("topic_key")
  bestProvider         String?  @map("best_provider")
  bestModel            String?  @map("best_model")
  routeCount           Int      @default(0) @map("route_count")
  successRate          Decimal  @default(0) @map("success_rate") @db.Decimal(6, 4)
  thumbsUpRate         Decimal  @default(0) @map("thumbs_up_rate") @db.Decimal(6, 4)
  thumbsDownRate       Decimal  @default(0) @map("thumbs_down_rate") @db.Decimal(6, 4)
  judgeVerifiedRate    Decimal  @default(0) @map("judge_verified_rate") @db.Decimal(6, 4)
  judgeEscalatedRate   Decimal  @default(0) @map("judge_escalated_rate") @db.Decimal(6, 4)
  fallbackSuccessRate  Decimal  @default(0) @map("fallback_success_rate") @db.Decimal(6, 4)
  weightedSuccessScore Decimal  @default(0) @map("weighted_success_score") @db.Decimal(6, 4)
  ambiguityScore       Decimal  @default(0) @map("ambiguity_score") @db.Decimal(6, 4)
  confidenceInProfile  Decimal  @default(0) @map("confidence_in_profile") @db.Decimal(6, 4)
  lastUpdated          DateTime @default(now()) @map("last_updated")

  @@unique([taskFamily, topicKey])
  @@index([weightedSuccessScore])
  @@map("router_topic_profiles")
}

model RoutingCalibrationSnapshot {
  id          String   @id @default(cuid())
  version     String
  windowDays  Int      @map("window_days")
  summary     Json
  promptHints Json     @map("prompt_hints")
  active      Boolean  @default(true)
  generatedAt DateTime @default(now()) @map("generated_at")

  @@index([generatedAt])
  @@index([active])
  @@map("routing_calibration_snapshots")
}

// ─── Smart Router Flagship — Model Intelligence Registry ───

/// Canonical registry of every model the router knows.
/// Distinct from the existing `RouterModelProfile` table which tracks per-task
/// learned metrics (per (provider,model,taskFamily,topicKey)). The registry
/// records identity, capabilities, economics, and lifecycle — one row per model.
model RouterModelRegistry {
  id                  String         @id @default(cuid())
  // identity
  provider            String // "OPENAI" | "ANTHROPIC" | "GEMINI" | "GROK" | "BEDROCK" | "DEEPSEEK" | "OLLAMA" | "LLAMACPP" | ...
  modelKey            String         @map("model_key") // wire-name e.g. "gpt-4o-2024-11-20"
  displayName         String         @map("display_name")
  family              String?
  connectorId         String?        @map("connector_id") // for cloud models — id from connector-service
  runtimeId           String?        @map("runtime_id") // for local models — id from ollama/llamacpp service
  // classification flags
  isLocal             Boolean        @default(false) @map("is_local")
  isRouterOnly        Boolean        @default(false) @map("is_router_only")
  isExecutionCapable  Boolean        @default(true) @map("is_execution_capable")
  lifecycle           ModelLifecycle @default(ACTIVE)
  // capabilities
  modalitiesIn        ModalityKind[] @default([]) @map("modalities_in")
  modalitiesOut       ModalityKind[] @default([]) @map("modalities_out")
  contextWindowTokens Int?           @map("context_window_tokens")
  maxOutputTokens     Int?           @map("max_output_tokens")
  domainTags          DomainTag[]    @default([]) @map("domain_tags")
  notRecommendedFor   DomainTag[]    @default([]) @map("not_recommended_for")
  // economics
  inputCostPer1M      Decimal?       @map("input_cost_per_1m") @db.Decimal(12, 6)
  outputCostPer1M     Decimal?       @map("output_cost_per_1m") @db.Decimal(12, 6)
  costConfidence      CostConfidence @default(UNKNOWN) @map("cost_confidence")
  costClass           CostClass?     @map("cost_class")
  // performance (rolled from telemetry)
  latencyP50Ms        Int?           @map("latency_p50_ms")
  latencyP95Ms        Int?           @map("latency_p95_ms")
  latencyClass        LatencyClass?  @map("latency_class")
  // quality / safety
  qualityTier         QualityTier    @default(B) @map("quality_tier")
  hallucinationRisk   Decimal?       @map("hallucination_risk") @db.Decimal(4, 3)
  judgeSuitability    Boolean        @default(false) @map("judge_suitability")
  searchSuitability   Boolean        @default(false) @map("search_suitability")
  fallbackSuitability Boolean        @default(true) @map("fallback_suitability")
  // privacy
  privacySupport      PrivacyClass   @default(CLOUD_PERMITTED) @map("privacy_support")
  // metadata
  metadataSource      String         @default("seed") @map("metadata_source") // seed | connector_sync | ollama_sync | llamacpp_sync | admin_override | learned
  externalCardUrl     String?        @map("external_card_url")
  notes               String?
  // ─── Semantic Router Flagship — Phase 3: Model Intelligence enrichment ───
  // All fields below are additive and nullable / default-falsy. They surface
  // richer capability / cost / latency / domain metadata for the AI Route
  // Planner (Phase 4), Fallback Executor (Phase 5), and Learning Loop
  // (Phase 9). They MUST stay backwards compatible — the v1 hot path does
  // not read any of them.
  //
  // Capability flags. Unknown capability = NULL (NOT false). The planner
  // treats null as "do not assume; do not select for high-risk".
  supportsStreaming         Boolean?  @default(false) @map("supports_streaming")
  supportsTools             Boolean?  @default(false) @map("supports_tools")
  supportsStructuredOutput  Boolean?  @default(false) @map("supports_structured_output")
  supportsVision            Boolean?  @default(false) @map("supports_vision")
  supportsAudioInput        Boolean?  @default(false) @map("supports_audio_input")
  supportsAudioOutput       Boolean?  @default(false) @map("supports_audio_output")
  supportsVideoInput        Boolean?  @default(false) @map("supports_video_input")
  supportsFileInput         Boolean?  @default(false) @map("supports_file_input")
  supportsEmbeddings        Boolean?  @default(false) @map("supports_embeddings")
  supportsLongContext       Boolean?  @default(false) @map("supports_long_context")
  // Token ceilings as exposed by upstream cards (already covered by
  // contextWindowTokens/maxOutputTokens above — these aliases let the
  // intelligence shape stay 1:1 with §6.1 of the flagship prompt).
  maxContextTokens          Int?      @map("max_context_tokens")
  maxOutputTokensIntel      Int?      @map("max_output_tokens_intel")
  // Domain / role guidance for the planner. String arrays so future
  // taxonomy additions don't require a new migration.
  domainStrengths           String[]  @default([]) @map("domain_strengths")
  roleStrengths             String[]  @default([]) @map("role_strengths")
  weakDomains               String[]  @default([]) @map("weak_domains")
  bestFor                   String[]  @default([]) @map("best_for")
  avoidFor                  String[]  @default([]) @map("avoid_for")
  languageStrengths         String[]  @default([]) @map("language_strengths")
  // Quality / cost / latency / privacy as STRING tier labels separate from
  // the existing typed enum columns. This keeps the canonical enum columns
  // untouched while giving the planner a free-form override hook.
  qualityTierLabel          String?   @map("quality_tier_label") // "BASIC" | "STANDARD" | "PRO" | "FRONTIER"
  costClassLabel            String?   @map("cost_class_label")   // "FREE" | "LOW" | "MEDIUM" | "HIGH" | "PREMIUM"
  costConfidenceLabel       String?   @map("cost_confidence_label") // "EXACT" | "ESTIMATED" | "UNKNOWN"
  estimatedInputCostPer1M   Float?    @map("estimated_input_cost_per_1m")
  estimatedOutputCostPer1M  Float?    @map("estimated_output_cost_per_1m")
  latencyClassLabel         String?   @map("latency_class_label") // "FAST" | "MEDIUM" | "SLOW"
  privacyClassLabel         String?   @map("privacy_class_label") // "local" | "cloud"
  // adminOverrideJson is the "freeze" block. Sync passes MUST NOT overwrite
  // any key present here. See `ModelIntelligenceService.applyOverrides()`
  // and `RouterSyncManager.applySnapshot()`.
  adminOverrideJson         Json?     @map("admin_override_json")
  lastEnrichedAt            DateTime? @map("last_enriched_at")
  // bookkeeping
  createdAt           DateTime       @default(now()) @map("created_at")
  updatedAt           DateTime       @updatedAt @map("updated_at")
  lastSyncedAt        DateTime?      @map("last_synced_at")

  overrides RouterAdminOverride[]

  @@unique([provider, modelKey])
  @@index([provider])
  @@index([lifecycle])
  @@index([isExecutionCapable, lifecycle])
  @@index([isRouterOnly])
  @@map("router_model_registry")
}

/// Phase 10: Learned-score table. Per-(profile, domain, taskFamily) success
/// metrics that the scoring engine reads to nudge ranking. Separate from
/// RouterModelRegistry so manual admin overrides stay clean and learning
/// can be bounded/rolled back without touching the canonical registry.
model RouterLearnedScore {
  id                String   @id @default(cuid())
  profileKey        String   @map("profile_key")
  domain            DomainTag
  taskFamily        String   @map("task_family")
  successRate       Decimal  @default(0.6) @map("success_rate") @db.Decimal(6, 4)
  feedbackPositive  Int      @default(0) @map("feedback_positive")
  feedbackNegative  Int      @default(0) @map("feedback_negative")
  judgeVerified     Int      @default(0) @map("judge_verified")
  judgeRevised      Int      @default(0) @map("judge_revised")
  judgeEscalated    Int      @default(0) @map("judge_escalated")
  fallbackTriggered Int      @default(0) @map("fallback_triggered")
  totalRoutes       Int      @default(0) @map("total_routes")
  lastUpdatedAt     DateTime @default(now()) @map("last_updated_at")
  createdAt         DateTime @default(now()) @map("created_at")

  @@unique([profileKey, domain, taskFamily])
  @@index([profileKey])
  @@index([domain])
  @@map("router_learned_scores")
}

/// Phase 9: Router workflows. Multi-step pipelines the router can choose
/// instead of (or in addition to) a single LLM model.
model RouterWorkflow {
  id               String       @id @default(cuid())
  workflowKey      String       @unique @map("workflow_key")
  kind             WorkflowKind
  displayName      String       @map("display_name")
  description      String?
  steps            Json
  defaultModelTier String       @default("A") @map("default_model_tier")
  isEnabled        Boolean      @default(true) @map("is_enabled")
  createdAt        DateTime     @default(now()) @map("created_at")
  updatedAt        DateTime     @updatedAt @map("updated_at")

  @@index([kind])
  @@map("router_workflows")
}

/// Phase 12: Circuit-breaker state per provider/runtime scope.
/// Survives container restarts so a flapping provider doesn't reset on
/// every redeploy. State transitions are managed by CircuitBreakerManager.
model RouterCircuitBreaker {
  id               String    @id @default(cuid())
  scope            String    @unique
  state            String
  failureCount     Int       @default(0) @map("failure_count")
  openedAt         DateTime? @map("opened_at")
  lastTransitionAt DateTime  @default(now()) @map("last_transition_at")
  createdAt        DateTime  @default(now()) @map("created_at")
  updatedAt        DateTime  @updatedAt @map("updated_at")

  @@map("router_circuit_breakers")
}

/// Phase 2: Domain/Career/Major Taxonomy. One row per professional role.
/// Routes use this to map (prompt → role → expected domain + privacy default).
model TaxonomyRole {
  id             String       @id @default(cuid())
  roleKey        String       @unique @map("role_key")
  displayName    String       @map("display_name")
  industryKey    String       @map("industry_key")
  domainKey      DomainTag    @map("domain_key")
  capabilities   String[]     @default([])
  privacyDefault PrivacyClass @default(CLOUD_PERMITTED) @map("privacy_default")
  createdAt      DateTime     @default(now()) @map("created_at")
  updatedAt      DateTime     @updatedAt @map("updated_at")

  @@index([industryKey])
  @@index([domainKey])
  @@map("taxonomy_roles")
}

/// Per-(profile, fieldName) admin override that survives upstream sync.
/// Sync workers MUST skip writing fields that have an active override.
model RouterAdminOverride {
  id         String   @id @default(cuid())
  profileId  String   @map("profile_id")
  fieldName  String   @map("field_name")
  fieldValue Json     @map("field_value")
  reason     String?
  setBy      String   @map("set_by")
  setAt      DateTime @default(now()) @map("set_at")
  isActive   Boolean  @default(true) @map("is_active")

  profile RouterModelRegistry @relation(fields: [profileId], references: [id], onDelete: Cascade)

  @@unique([profileId, fieldName])
  @@index([profileId])
  @@index([isActive])
  @@map("router_admin_overrides")
}
