model FeatureFlag { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt key String? scope String @default("global") tenantId String? userId String? name String description String enabled Boolean @default(false) // v2 targeting engine fields (task #53). All nullable / defaulted so // legacy rows continue to evaluate via boolean `enabled` only. rules Json? variants Json[] @default([]) enabledFrom DateTime? enabledUntil DateTime? parentFlagId String? parentFlag FeatureFlag? @relation("FeatureFlagDependency", fields: [parentFlagId], references: [id], onDelete: SetNull) childFlags FeatureFlag[] @relation("FeatureFlagDependency") audits FeatureFlagAudit[] events AnalyticsEvent[] filters FeatureFlagFilter[] @@unique([name, description]) @@index([key, scope], name: "feature_flag_key_scope_idx") @@index([tenantId, key], name: "feature_flag_tenant_key_idx") @@index([userId, key], name: "feature_flag_user_key_idx") @@index([parentFlagId], name: "feature_flag_parent_idx") } model FeatureFlagFilter { id String @id @default(cuid()) createdAt DateTime @default(now()) featureFlagId String featureFlag FeatureFlag @relation(fields: [featureFlagId], references: [id], onDelete: Cascade) type String // [FeatureFlagsFilterType] percentage, darkMode, language.is, page.is, analytics.browser.is... value String? // 0-100, true, false, en, de, fr, ... action String? } /// Append-only audit trail for every mutation of a FeatureFlag (create / update / delete / toggle). /// Never UPDATE or DELETE rows from app code — preserves tamper-resistance for compliance. model FeatureFlagAudit { id String @id @default(cuid()) flagId String flag FeatureFlag @relation(fields: [flagId], references: [id], onDelete: Cascade) actorUserId String? action String // create | update | delete | toggle oldValue Json? newValue Json? createdAt DateTime @default(now()) @@index([flagId, createdAt], name: "feature_flag_audit_flag_idx") @@index([actorUserId, createdAt], name: "feature_flag_audit_actor_idx") }