diff --git a/src/Orbit.Infrastructure/Migrations/20260712120428_AddUserHandleUniqueIndex.Designer.cs b/src/Orbit.Infrastructure/Migrations/20260712120428_AddUserHandleUniqueIndex.Designer.cs
new file mode 100644
index 00000000..90728650
--- /dev/null
+++ b/src/Orbit.Infrastructure/Migrations/20260712120428_AddUserHandleUniqueIndex.Designer.cs
@@ -0,0 +1,2566 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Orbit.Infrastructure.Persistence;
+
+#nullable disable
+
+namespace Orbit.Infrastructure.Migrations
+{
+ [DbContext(typeof(OrbitDbContext))]
+ [Migration("20260712120428_AddUserHandleUniqueIndex")]
+ partial class AddUserHandleUniqueIndex
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "10.0.9")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("HabitGoals", b =>
+ {
+ b.Property("GoalId")
+ .HasColumnType("uuid");
+
+ b.Property("HabitId")
+ .HasColumnType("uuid");
+
+ b.HasKey("GoalId", "HabitId");
+
+ b.HasIndex("HabitId");
+
+ b.ToTable("HabitGoals");
+ });
+
+ modelBuilder.Entity("HabitTags", b =>
+ {
+ b.Property("HabitId")
+ .HasColumnType("uuid");
+
+ b.Property("TagId")
+ .HasColumnType("uuid");
+
+ b.HasKey("HabitId", "TagId");
+
+ b.HasIndex("TagId");
+
+ b.ToTable("HabitTags");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.AccountabilityCheckIn", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Date")
+ .HasColumnType("date");
+
+ b.Property("Note")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("PairId")
+ .HasColumnType("uuid");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PairId", "CreatedAtUtc");
+
+ b.HasIndex("PairId", "UserId", "Date")
+ .IsUnique();
+
+ b.ToTable("AccountabilityCheckIns");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.AccountabilityPair", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AcceptedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("AddresseeId")
+ .HasColumnType("uuid");
+
+ b.Property("Cadence")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EndedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("RequesterId")
+ .HasColumnType("uuid");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AddresseeId");
+
+ b.HasIndex("RequesterId");
+
+ b.ToTable("AccountabilityPairs");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.AccountabilityPairHabit", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("HabitId")
+ .HasColumnType("uuid");
+
+ b.Property("PairId")
+ .HasColumnType("uuid");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("HabitId");
+
+ b.HasIndex("PairId", "UserId", "HabitId")
+ .IsUnique();
+
+ b.ToTable("AccountabilityPairHabits");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.AgentAuditLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AuthMethod")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("CapabilityId")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("CorrelationId")
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Error")
+ .HasMaxLength(500)
+ .HasColumnType("character varying(500)");
+
+ b.Property("OutcomeStatus")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("PolicyDecision")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("RedactedArguments")
+ .HasMaxLength(4000)
+ .HasColumnType("character varying(4000)");
+
+ b.Property("RiskClass")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("ShadowPolicyDecision")
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("ShadowReason")
+ .HasMaxLength(500)
+ .HasColumnType("character varying(500)");
+
+ b.Property("SourceName")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("Summary")
+ .HasColumnType("text");
+
+ b.Property("Surface")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("TargetId")
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("TargetName")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CapabilityId", "CreatedAtUtc");
+
+ b.HasIndex("UserId", "CreatedAtUtc");
+
+ b.ToTable("AgentAuditLogs");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.AgentStepUpChallengeState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AttemptCount")
+ .HasColumnType("integer");
+
+ b.Property("CodeHash")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ExpiresAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PendingOperationId")
+ .HasColumnType("uuid");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("VerifiedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "PendingOperationId", "CreatedAtUtc");
+
+ b.ToTable("AgentStepUpChallenges");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.AiFactExtractionBatch", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("BatchId")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("CompletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("InputFileId")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("OutputFileId")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BatchId")
+ .IsUnique();
+
+ b.HasIndex("Status");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AiFactExtractionBatches");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.AiUsageDaily", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CachedTokens")
+ .HasColumnType("bigint");
+
+ b.Property("Calls")
+ .HasColumnType("bigint");
+
+ b.Property("CompletionTokens")
+ .HasColumnType("bigint");
+
+ b.Property("CostUsd")
+ .HasColumnType("numeric");
+
+ b.Property("Date")
+ .HasColumnType("date");
+
+ b.Property("Model")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("PromptTokens")
+ .HasColumnType("bigint");
+
+ b.Property("Purpose")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("TotalTokens")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Date", "Model", "Purpose")
+ .IsUnique();
+
+ b.ToTable("AiUsageDaily");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.ApiKey", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ExpiresAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsReadOnly")
+ .HasColumnType("boolean");
+
+ b.Property("IsRevoked")
+ .HasColumnType("boolean");
+
+ b.Property("KeyHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("KeyPrefix")
+ .IsRequired()
+ .HasMaxLength(12)
+ .HasColumnType("character varying(12)");
+
+ b.Property("LastUsedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("Scopes")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("jsonb")
+ .HasDefaultValueSql("'[]'::jsonb");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("KeyPrefix");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("ApiKeys");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.AppConfig", b =>
+ {
+ b.Property("Key")
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("Description")
+ .HasMaxLength(500)
+ .HasColumnType("character varying(500)");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("character varying(500)");
+
+ b.HasKey("Key");
+
+ b.ToTable("AppConfigs");
+
+ b.HasData(
+ new
+ {
+ Key = "MaxUserFacts",
+ Description = "Maximum number of facts the AI can remember per user",
+ Value = "50"
+ },
+ new
+ {
+ Key = "MaxHabitDepth",
+ Description = "Maximum nesting depth for sub-habits",
+ Value = "5"
+ },
+ new
+ {
+ Key = "MaxTagsPerHabit",
+ Description = "Maximum number of tags per habit",
+ Value = "5"
+ },
+ new
+ {
+ Key = "ReferralRewardDays",
+ Description = "Days of Pro added per successful referral",
+ Value = "10"
+ },
+ new
+ {
+ Key = "MaxReferrals",
+ Description = "Maximum successful referrals per user",
+ Value = "10"
+ },
+ new
+ {
+ Key = "MinSupportedVersion",
+ Description = "Minimum supported client app version; clients below this receive HTTP 426",
+ Value = "0.0.0"
+ });
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.AppFeatureFlag", b =>
+ {
+ b.Property("Key")
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("Description")
+ .HasMaxLength(500)
+ .HasColumnType("character varying(500)");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("PlanRequirement")
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Key");
+
+ b.ToTable("AppFeatureFlags");
+
+ b.HasData(
+ new
+ {
+ Key = "offline_mode",
+ Description = "Enable offline mode with background sync",
+ Enabled = true,
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "ai_chat",
+ Description = "AI chat assistant",
+ Enabled = true,
+ PlanRequirement = "Free",
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "ai_summary",
+ Description = "AI daily summary",
+ Enabled = true,
+ PlanRequirement = "Pro",
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "ai_retrospective",
+ Description = "AI retrospective analysis",
+ Enabled = true,
+ PlanRequirement = "YearlyPro",
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "sub_habits",
+ Description = "Sub-habit nesting",
+ Enabled = true,
+ PlanRequirement = "Pro",
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "goal_tracking",
+ Description = "Goal tracking with progress",
+ Enabled = true,
+ PlanRequirement = "Pro",
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "push_notifications",
+ Description = "Push notification reminders",
+ Enabled = true,
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "scheduled_reminders",
+ Description = "Custom scheduled reminders",
+ Enabled = true,
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "slip_alerts",
+ Description = "Slip detection alerts",
+ Enabled = true,
+ PlanRequirement = "Pro",
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "checklist_templates",
+ Description = "Reusable checklist templates",
+ Enabled = true,
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "habit_duplication",
+ Description = "Duplicate habits",
+ Enabled = true,
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "bulk_operations",
+ Description = "Bulk create/delete/log habits",
+ Enabled = true,
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "calendar_integration",
+ Description = "Google Calendar integration",
+ Enabled = true,
+ PlanRequirement = "Pro",
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ },
+ new
+ {
+ Key = "api_keys",
+ Description = "Personal API keys",
+ Enabled = true,
+ PlanRequirement = "Pro",
+ UpdatedAtUtc = new DateTime(2026, 4, 1, 0, 0, 0, 0, DateTimeKind.Utc)
+ });
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.BlockedUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("BlockedId")
+ .HasColumnType("uuid");
+
+ b.Property("BlockerId")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BlockedId");
+
+ b.HasIndex("BlockerId", "BlockedId")
+ .IsUnique();
+
+ b.ToTable("BlockedUsers");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.Challenge", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CompletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatorId")
+ .HasColumnType("uuid");
+
+ b.Property("DeletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasMaxLength(2000)
+ .HasColumnType("character varying(2000)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("JoinCode")
+ .IsRequired()
+ .HasMaxLength(16)
+ .HasColumnType("character varying(16)");
+
+ b.Property("PeriodEndUtc")
+ .HasColumnType("date");
+
+ b.Property("PeriodStartUtc")
+ .HasColumnType("date");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("TargetCount")
+ .HasColumnType("integer");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatorId");
+
+ b.HasIndex("JoinCode")
+ .IsUnique();
+
+ b.ToTable("Challenges");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.ChallengeParticipant", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("ChallengeId")
+ .HasColumnType("uuid");
+
+ b.Property("JoinedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LeftAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("ChallengeId", "UserId")
+ .IsUnique()
+ .HasFilter("\"LeftAtUtc\" IS NULL");
+
+ b.ToTable("ChallengeParticipants");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.ChallengeParticipantHabit", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("ChallengeParticipantId")
+ .HasColumnType("uuid");
+
+ b.Property("HabitId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("HabitId");
+
+ b.HasIndex("ChallengeParticipantId", "HabitId")
+ .IsUnique();
+
+ b.ToTable("ChallengeParticipantHabits");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.ChecklistTemplate", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("Items")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("jsonb")
+ .HasDefaultValueSql("'[]'::jsonb");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("UserId", "IsDeleted");
+
+ b.ToTable("ChecklistTemplates");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.Cheer", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("HabitId")
+ .HasColumnType("uuid");
+
+ b.Property("Note")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("RecipientId")
+ .HasColumnType("uuid");
+
+ b.Property("SenderId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("HabitId");
+
+ b.HasIndex("RecipientId");
+
+ b.HasIndex("SenderId", "CreatedAtUtc");
+
+ b.ToTable("Cheers");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.ContentBlock", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Category")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("Content")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Key")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("Locale")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("character varying(10)");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Key", "Locale")
+ .IsUnique();
+
+ b.ToTable("ContentBlocks");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.DistributedRateLimitBucket", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Count")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PartitionKey")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("PolicyName")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("WindowEndsAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("WindowStartUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PolicyName", "PartitionKey", "WindowStartUtc")
+ .IsUnique();
+
+ b.ToTable("DistributedRateLimitBuckets");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.FriendFeedEvent", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AchievementId")
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("ActorUserId")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("Value")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ActorUserId", "AchievementId")
+ .IsUnique()
+ .HasFilter("\"AchievementId\" IS NOT NULL");
+
+ b.HasIndex("ActorUserId", "CreatedAtUtc", "Id")
+ .IsDescending(false, true, true);
+
+ b.HasIndex("ActorUserId", "Type", "Value")
+ .IsUnique()
+ .HasFilter("\"AchievementId\" IS NULL");
+
+ b.ToTable("FriendFeedEvents");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.Friendship", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AddresseeId")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("RequesterId")
+ .HasColumnType("uuid");
+
+ b.Property("RespondedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AddresseeId");
+
+ b.HasIndex("RequesterId");
+
+ b.ToTable("Friendships");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.Goal", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CompletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CurrentValue")
+ .HasColumnType("numeric");
+
+ b.Property("Deadline")
+ .HasColumnType("date");
+
+ b.Property("DeletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("Position")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .HasColumnType("integer");
+
+ b.Property("StreakSyncedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TargetValue")
+ .HasColumnType("numeric");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.Property("Unit")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("xmin")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("xid")
+ .HasColumnName("xmin");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("UserId", "IsDeleted");
+
+ b.ToTable("Goals");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.GoalProgressLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("GoalId")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("Note")
+ .HasColumnType("text");
+
+ b.Property("PreviousValue")
+ .HasColumnType("numeric");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Value")
+ .HasColumnType("numeric");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GoalId");
+
+ b.HasIndex("GoalId", "IsDeleted");
+
+ b.ToTable("GoalProgressLogs");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.GoogleCalendarSyncSuggestion", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("DiscoveredAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DismissedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("GoogleEventId")
+ .IsRequired()
+ .HasMaxLength(1024)
+ .HasColumnType("character varying(1024)");
+
+ b.Property("ImportedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ImportedHabitId")
+ .HasColumnType("uuid");
+
+ b.Property("RawEventJson")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("StartDateUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("UserId", "GoogleEventId")
+ .IsUnique();
+
+ b.HasIndex("UserId", "DismissedAtUtc", "ImportedAtUtc");
+
+ b.ToTable("GoogleCalendarSyncSuggestions");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.Habit", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("ChecklistItems")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("jsonb")
+ .HasDefaultValueSql("'[]'::jsonb");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Days")
+ .IsRequired()
+ .HasColumnType("text[]");
+
+ b.Property("DeletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("DueDate")
+ .HasColumnType("date");
+
+ b.Property("DueEndTime")
+ .HasColumnType("time without time zone");
+
+ b.Property("DueTime")
+ .HasColumnType("time without time zone");
+
+ b.Property("Emoji")
+ .HasColumnType("text");
+
+ b.Property("EndDate")
+ .HasColumnType("date");
+
+ b.Property("FrequencyQuantity")
+ .HasColumnType("integer");
+
+ b.Property("FrequencyUnit")
+ .HasColumnType("integer");
+
+ b.Property("GoogleEventId")
+ .HasMaxLength(1024)
+ .HasColumnType("character varying(1024)");
+
+ b.Property("IsBadHabit")
+ .HasColumnType("boolean");
+
+ b.Property("IsCompleted")
+ .HasColumnType("boolean");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("IsFlexible")
+ .HasColumnType("boolean");
+
+ b.Property("IsGeneral")
+ .HasColumnType("boolean");
+
+ b.Property("OriginalDayOfMonth")
+ .HasColumnType("integer");
+
+ b.Property("ParentHabitId")
+ .HasColumnType("uuid");
+
+ b.Property("Position")
+ .HasColumnType("integer");
+
+ b.Property("ReminderEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("ReminderTimes")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("jsonb")
+ .HasDefaultValueSql("'[15]'::jsonb");
+
+ b.Property("ScheduledReminders")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("jsonb")
+ .HasDefaultValueSql("'[]'::jsonb");
+
+ b.Property("SlipAlertEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentHabitId");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("UserId", "GoogleEventId")
+ .IsUnique()
+ .HasFilter("\"GoogleEventId\" IS NOT NULL AND \"IsDeleted\" = FALSE");
+
+ b.HasIndex("UserId", "IsDeleted");
+
+ b.ToTable("Habits");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.HabitLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Date")
+ .HasColumnType("date");
+
+ b.Property("DeletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("HabitId")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("Note")
+ .HasColumnType("text");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Value")
+ .HasColumnType("numeric");
+
+ b.HasKey("Id");
+
+ b.HasIndex(new[] { "HabitId", "Date" }, "IX_HabitLogs_HabitId_Date");
+
+ b.HasIndex(new[] { "HabitId", "Date" }, "IX_HabitLogs_HabitId_Date_Completed")
+ .IsUnique()
+ .HasFilter("\"Value\" > 0 AND NOT \"IsDeleted\"");
+
+ b.ToTable("HabitLogs");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.Notification", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Body")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("HabitId")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("IsRead")
+ .HasColumnType("boolean");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Url")
+ .HasColumnType("text");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Url")
+ .HasFilter("\"Url\" IS NOT NULL");
+
+ b.HasIndex("UserId", "CreatedAtUtc")
+ .IsDescending(false, true);
+
+ b.HasIndex("UserId", "IsDeleted");
+
+ b.HasIndex("UserId", "IsRead");
+
+ b.ToTable("Notifications");
+ });
+
+ modelBuilder.Entity("Orbit.Domain.Entities.PendingAgentOperationState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("ArgumentsJson")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("CapabilityId")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("ConfirmationRequirement")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("ConfirmationTokenHash")
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("ConfirmedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ConsumedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("ExpiresAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("OperationFingerprint")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("OperationId")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("RiskClass")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("StepUpSatisfiedAtUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Summary")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("character varying(500)");
+
+ b.Property