diff --git a/src/ClaudeDo.Data/Migrations/20260601140000_NormalizeListIdFormat.Designer.cs b/src/ClaudeDo.Data/Migrations/20260601140000_NormalizeListIdFormat.Designer.cs
new file mode 100644
index 0000000..77e60e5
--- /dev/null
+++ b/src/ClaudeDo.Data/Migrations/20260601140000_NormalizeListIdFormat.Designer.cs
@@ -0,0 +1,607 @@
+//
+using System;
+using ClaudeDo.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace ClaudeDo.Data.Migrations
+{
+ [DbContext(typeof(ClaudeDoDbContext))]
+ [Migration("20260601140000_NormalizeListIdFormat")]
+ partial class NormalizeListIdFormat
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "8.0.11");
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.AppSettingsEntity", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("INTEGER")
+ .HasColumnName("id");
+
+ b.Property("CentralWorktreeRoot")
+ .HasColumnType("TEXT")
+ .HasColumnName("central_worktree_root");
+
+ b.Property("DefaultClaudeInstructions")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasDefaultValue("")
+ .HasColumnName("default_claude_instructions");
+
+ b.Property("DefaultMaxTurns")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(30)
+ .HasColumnName("default_max_turns");
+
+ b.Property("DefaultModel")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasDefaultValue("sonnet")
+ .HasColumnName("default_model");
+
+ b.Property("DefaultPermissionMode")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasDefaultValue("bypassPermissions")
+ .HasColumnName("default_permission_mode");
+
+ b.Property("MaxParallelExecutions")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(1)
+ .HasColumnName("max_parallel_executions");
+
+ b.Property("RepoImportFolders")
+ .HasColumnType("TEXT")
+ .HasColumnName("repo_import_folders");
+
+ b.Property("WorktreeAutoCleanupDays")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(7)
+ .HasColumnName("worktree_auto_cleanup_days");
+
+ b.Property("WorktreeAutoCleanupEnabled")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(false)
+ .HasColumnName("worktree_auto_cleanup_enabled");
+
+ b.Property("WorktreeStrategy")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasDefaultValue("sibling")
+ .HasColumnName("worktree_strategy");
+
+ b.HasKey("Id");
+
+ b.ToTable("app_settings", (string)null);
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ DefaultClaudeInstructions = "",
+ DefaultMaxTurns = 100,
+ DefaultModel = "sonnet",
+ DefaultPermissionMode = "auto",
+ MaxParallelExecutions = 1,
+ WorktreeAutoCleanupDays = 7,
+ WorktreeAutoCleanupEnabled = false,
+ WorktreeStrategy = "sibling"
+ });
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.ListConfigEntity", b =>
+ {
+ b.Property("ListId")
+ .HasColumnType("TEXT")
+ .HasColumnName("list_id");
+
+ b.Property("AgentPath")
+ .HasColumnType("TEXT")
+ .HasColumnName("agent_path");
+
+ b.Property("Model")
+ .HasColumnType("TEXT")
+ .HasColumnName("model");
+
+ b.Property("SystemPrompt")
+ .HasColumnType("TEXT")
+ .HasColumnName("system_prompt");
+
+ b.HasKey("ListId");
+
+ b.ToTable("list_config", (string)null);
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.ListEntity", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT")
+ .HasColumnName("id");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("created_at");
+
+ b.Property("DefaultCommitType")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasDefaultValue("chore")
+ .HasColumnName("default_commit_type");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("name");
+
+ b.Property("SortOrder")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(0)
+ .HasColumnName("sort_order");
+
+ b.Property("WorkingDir")
+ .HasColumnType("TEXT")
+ .HasColumnName("working_dir");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SortOrder")
+ .HasDatabaseName("idx_lists_sort");
+
+ b.ToTable("lists", (string)null);
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.PrimeScheduleEntity", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT")
+ .HasColumnName("id");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("created_at");
+
+ b.Property("Enabled")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(true)
+ .HasColumnName("enabled");
+
+ b.Property("EndDate")
+ .HasColumnType("TEXT")
+ .HasColumnName("end_date");
+
+ b.Property("LastRunAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("last_run_at");
+
+ b.Property("PromptOverride")
+ .HasColumnType("TEXT")
+ .HasColumnName("prompt_override");
+
+ b.Property("StartDate")
+ .HasColumnType("TEXT")
+ .HasColumnName("start_date");
+
+ b.Property("TimeOfDay")
+ .HasColumnType("TEXT")
+ .HasColumnName("time_of_day");
+
+ b.Property("WorkdaysOnly")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(true)
+ .HasColumnName("workdays_only");
+
+ b.HasKey("Id");
+
+ b.ToTable("prime_schedules", (string)null);
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.SubtaskEntity", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT")
+ .HasColumnName("id");
+
+ b.Property("Completed")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(false)
+ .HasColumnName("completed");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("created_at");
+
+ b.Property("OrderNum")
+ .HasColumnType("INTEGER")
+ .HasColumnName("order_num");
+
+ b.Property("TaskId")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("task_id");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("title");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TaskId")
+ .HasDatabaseName("idx_subtasks_task_id");
+
+ b.ToTable("subtasks", (string)null);
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.TaskEntity", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT")
+ .HasColumnName("id");
+
+ b.Property("AgentPath")
+ .HasColumnType("TEXT")
+ .HasColumnName("agent_path");
+
+ b.Property("BlockedByTaskId")
+ .HasColumnType("TEXT")
+ .HasColumnName("blocked_by_task_id");
+
+ b.Property("CommitType")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasDefaultValue("chore")
+ .HasColumnName("commit_type");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("created_at");
+
+ b.Property("CreatedBy")
+ .HasColumnType("TEXT")
+ .HasColumnName("created_by");
+
+ b.Property("Description")
+ .HasColumnType("TEXT")
+ .HasColumnName("description");
+
+ b.Property("FinishedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("finished_at");
+
+ b.Property("IsMyDay")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(false)
+ .HasColumnName("is_my_day");
+
+ b.Property("IsStarred")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(false)
+ .HasColumnName("is_starred");
+
+ b.Property("ListId")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("list_id");
+
+ b.Property("LogPath")
+ .HasColumnType("TEXT")
+ .HasColumnName("log_path");
+
+ b.Property("Model")
+ .HasColumnType("TEXT")
+ .HasColumnName("model");
+
+ b.Property("Notes")
+ .HasColumnType("TEXT")
+ .HasColumnName("notes");
+
+ b.Property("ParentTaskId")
+ .HasColumnType("TEXT")
+ .HasColumnName("parent_task_id");
+
+ b.Property("PlanningFinalizedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("planning_finalized_at");
+
+ b.Property("PlanningPhase")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasDefaultValue("none")
+ .HasColumnName("planning_phase");
+
+ b.Property("PlanningSessionId")
+ .HasColumnType("TEXT")
+ .HasColumnName("planning_session_id");
+
+ b.Property("PlanningSessionToken")
+ .HasColumnType("TEXT")
+ .HasColumnName("planning_session_token");
+
+ b.Property("Result")
+ .HasColumnType("TEXT")
+ .HasColumnName("result");
+
+ b.Property("ScheduledFor")
+ .HasColumnType("TEXT")
+ .HasColumnName("scheduled_for");
+
+ b.Property("SortOrder")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(0)
+ .HasColumnName("sort_order");
+
+ b.Property("StartedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("started_at");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("status");
+
+ b.Property("SystemPrompt")
+ .HasColumnType("TEXT")
+ .HasColumnName("system_prompt");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("title");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BlockedByTaskId")
+ .HasDatabaseName("idx_tasks_blocked_by");
+
+ b.HasIndex("ListId")
+ .HasDatabaseName("idx_tasks_list_id");
+
+ b.HasIndex("ParentTaskId")
+ .HasDatabaseName("idx_tasks_parent_task_id");
+
+ b.HasIndex("Status")
+ .HasDatabaseName("idx_tasks_status");
+
+ b.HasIndex("ListId", "SortOrder")
+ .HasDatabaseName("idx_tasks_list_sort");
+
+ b.ToTable("tasks", (string)null);
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.TaskRunEntity", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT")
+ .HasColumnName("id");
+
+ b.Property("ErrorMarkdown")
+ .HasColumnType("TEXT")
+ .HasColumnName("error_markdown");
+
+ b.Property("ExitCode")
+ .HasColumnType("INTEGER")
+ .HasColumnName("exit_code");
+
+ b.Property("FinishedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("finished_at");
+
+ b.Property("IsRetry")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(false)
+ .HasColumnName("is_retry");
+
+ b.Property("LogPath")
+ .HasColumnType("TEXT")
+ .HasColumnName("log_path");
+
+ b.Property("Prompt")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("prompt");
+
+ b.Property("ResultMarkdown")
+ .HasColumnType("TEXT")
+ .HasColumnName("result_markdown");
+
+ b.Property("RunNumber")
+ .HasColumnType("INTEGER")
+ .HasColumnName("run_number");
+
+ b.Property("SessionId")
+ .HasColumnType("TEXT")
+ .HasColumnName("session_id");
+
+ b.Property("StartedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("started_at");
+
+ b.Property("StructuredOutputJson")
+ .HasColumnType("TEXT")
+ .HasColumnName("structured_output");
+
+ b.Property("TaskId")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("task_id");
+
+ b.Property("TokensIn")
+ .HasColumnType("INTEGER")
+ .HasColumnName("tokens_in");
+
+ b.Property("TokensOut")
+ .HasColumnType("INTEGER")
+ .HasColumnName("tokens_out");
+
+ b.Property("TurnCount")
+ .HasColumnType("INTEGER")
+ .HasColumnName("turn_count");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TaskId")
+ .HasDatabaseName("idx_task_runs_task_id");
+
+ b.ToTable("task_runs", (string)null);
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.WorktreeEntity", b =>
+ {
+ b.Property("TaskId")
+ .HasColumnType("TEXT")
+ .HasColumnName("task_id");
+
+ b.Property("BaseCommit")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("base_commit");
+
+ b.Property("BranchName")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("branch_name");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT")
+ .HasColumnName("created_at");
+
+ b.Property("DiffStat")
+ .HasColumnType("TEXT")
+ .HasColumnName("diff_stat");
+
+ b.Property("HeadCommit")
+ .HasColumnType("TEXT")
+ .HasColumnName("head_commit");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasColumnType("TEXT")
+ .HasColumnName("path");
+
+ b.Property("State")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasDefaultValue("active")
+ .HasColumnName("state");
+
+ b.HasKey("TaskId");
+
+ b.ToTable("worktrees", (string)null);
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.ListConfigEntity", b =>
+ {
+ b.HasOne("ClaudeDo.Data.Models.ListEntity", "List")
+ .WithOne("Config")
+ .HasForeignKey("ClaudeDo.Data.Models.ListConfigEntity", "ListId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("List");
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.SubtaskEntity", b =>
+ {
+ b.HasOne("ClaudeDo.Data.Models.TaskEntity", "Task")
+ .WithMany("Subtasks")
+ .HasForeignKey("TaskId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Task");
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.TaskEntity", b =>
+ {
+ b.HasOne("ClaudeDo.Data.Models.TaskEntity", null)
+ .WithMany()
+ .HasForeignKey("BlockedByTaskId")
+ .OnDelete(DeleteBehavior.SetNull);
+
+ b.HasOne("ClaudeDo.Data.Models.ListEntity", "List")
+ .WithMany("Tasks")
+ .HasForeignKey("ListId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("ClaudeDo.Data.Models.TaskEntity", "Parent")
+ .WithMany("Children")
+ .HasForeignKey("ParentTaskId")
+ .OnDelete(DeleteBehavior.Restrict);
+
+ b.Navigation("List");
+
+ b.Navigation("Parent");
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.TaskRunEntity", b =>
+ {
+ b.HasOne("ClaudeDo.Data.Models.TaskEntity", "Task")
+ .WithMany("Runs")
+ .HasForeignKey("TaskId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Task");
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.WorktreeEntity", b =>
+ {
+ b.HasOne("ClaudeDo.Data.Models.TaskEntity", "Task")
+ .WithOne("Worktree")
+ .HasForeignKey("ClaudeDo.Data.Models.WorktreeEntity", "TaskId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Task");
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.ListEntity", b =>
+ {
+ b.Navigation("Config");
+
+ b.Navigation("Tasks");
+ });
+
+ modelBuilder.Entity("ClaudeDo.Data.Models.TaskEntity", b =>
+ {
+ b.Navigation("Children");
+
+ b.Navigation("Runs");
+
+ b.Navigation("Subtasks");
+
+ b.Navigation("Worktree");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/ClaudeDo.Data/Migrations/20260601140000_NormalizeListIdFormat.cs b/src/ClaudeDo.Data/Migrations/20260601140000_NormalizeListIdFormat.cs
new file mode 100644
index 0000000..82e1062
--- /dev/null
+++ b/src/ClaudeDo.Data/Migrations/20260601140000_NormalizeListIdFormat.cs
@@ -0,0 +1,52 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace ClaudeDo.Data.Migrations
+{
+ ///
+ public partial class NormalizeListIdFormat : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ // SQLite: PRAGMA foreign_keys must run outside a transaction.
+ migrationBuilder.Sql("PRAGMA foreign_keys = OFF;", suppressTransaction: true);
+
+ // Normalize tasks.list_id: 32-char compact hex → 36-char dashed UUID
+ migrationBuilder.Sql("""
+ UPDATE tasks
+ SET list_id = substr(list_id,1,8)||'-'||substr(list_id,9,4)||'-'||substr(list_id,13,4)||'-'||substr(list_id,17,4)||'-'||substr(list_id,21,12)
+ WHERE length(list_id) = 32;
+ """);
+
+ // Normalize list_config.list_id (also the PK of that table)
+ migrationBuilder.Sql("""
+ UPDATE list_config
+ SET list_id = substr(list_id,1,8)||'-'||substr(list_id,9,4)||'-'||substr(list_id,13,4)||'-'||substr(list_id,17,4)||'-'||substr(list_id,21,12)
+ WHERE length(list_id) = 32;
+ """);
+
+ // Normalize lists.id (PK — must come last)
+ migrationBuilder.Sql("""
+ UPDATE lists
+ SET id = substr(id,1,8)||'-'||substr(id,9,4)||'-'||substr(id,13,4)||'-'||substr(id,17,4)||'-'||substr(id,21,12)
+ WHERE length(id) = 32;
+ """);
+
+ migrationBuilder.Sql("PRAGMA foreign_keys = ON;", suppressTransaction: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql("PRAGMA foreign_keys = OFF;", suppressTransaction: true);
+
+ migrationBuilder.Sql("UPDATE tasks SET list_id = replace(list_id,'-','') WHERE length(list_id) = 36;");
+ migrationBuilder.Sql("UPDATE list_config SET list_id = replace(list_id,'-','') WHERE length(list_id) = 36;");
+ migrationBuilder.Sql("UPDATE lists SET id = replace(id,'-','') WHERE length(id) = 36;");
+
+ migrationBuilder.Sql("PRAGMA foreign_keys = ON;", suppressTransaction: true);
+ }
+ }
+}
diff --git a/src/ClaudeDo.Data/Seeding/DefaultListsSeeder.cs b/src/ClaudeDo.Data/Seeding/DefaultListsSeeder.cs
index e3ec03d..ac579b6 100644
--- a/src/ClaudeDo.Data/Seeding/DefaultListsSeeder.cs
+++ b/src/ClaudeDo.Data/Seeding/DefaultListsSeeder.cs
@@ -15,7 +15,7 @@ public static class DefaultListsSeeder
{
ctx.Lists.Add(new ListEntity
{
- Id = Guid.NewGuid().ToString("N"),
+ Id = Guid.NewGuid().ToString(),
Name = name,
CreatedAt = now,
});