Files
ClaudeDo/src/ClaudeDo.Data/Configuration/TaskEntityConfiguration.cs
mika kuns 623ebf147b refactor(tags): remove tag entity and all references
Drops TagEntity, TagRepository, and tag wiring across data layer, worker,
and UI. Adds RemoveTags migration to clean up schema.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 08:07:24 +02:00

122 lines
5.6 KiB
C#

using ClaudeDo.Data.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
namespace ClaudeDo.Data.Configuration;
public class TaskEntityConfiguration : IEntityTypeConfiguration<TaskEntity>
{
private static string StatusToString(TaskStatus v)
=> v switch
{
TaskStatus.Idle => "idle",
TaskStatus.Queued => "queued",
TaskStatus.Running => "running",
TaskStatus.Done => "done",
TaskStatus.Failed => "failed",
TaskStatus.Cancelled => "cancelled",
_ => throw new ArgumentOutOfRangeException(nameof(v)),
};
private static TaskStatus StatusFromString(string v)
=> v switch
{
"idle" => TaskStatus.Idle,
"queued" => TaskStatus.Queued,
"running" => TaskStatus.Running,
"done" => TaskStatus.Done,
"failed" => TaskStatus.Failed,
"cancelled" => TaskStatus.Cancelled,
_ => throw new ArgumentOutOfRangeException(nameof(v)),
};
private static readonly ValueConverter<TaskStatus, string> StatusConverter =
new(v => StatusToString(v), v => StatusFromString(v));
private static string PhaseToString(PlanningPhase v)
=> v switch
{
PlanningPhase.None => "none",
PlanningPhase.Active => "active",
PlanningPhase.Finalized => "finalized",
_ => throw new ArgumentOutOfRangeException(nameof(v)),
};
private static PlanningPhase PhaseFromString(string v)
=> v switch
{
"none" => PlanningPhase.None,
"active" => PlanningPhase.Active,
"finalized" => PlanningPhase.Finalized,
_ => throw new ArgumentOutOfRangeException(nameof(v)),
};
private static readonly ValueConverter<PlanningPhase, string> PhaseConverter =
new(v => PhaseToString(v), v => PhaseFromString(v));
public void Configure(EntityTypeBuilder<TaskEntity> builder)
{
builder.ToTable("tasks");
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).HasColumnName("id");
builder.Property(t => t.ListId).HasColumnName("list_id").IsRequired();
builder.Property(t => t.Title).HasColumnName("title").IsRequired();
builder.Property(t => t.Description).HasColumnName("description");
builder.Property(t => t.Status).HasColumnName("status").IsRequired()
.HasConversion(StatusConverter);
builder.Property(t => t.PlanningPhase).HasColumnName("planning_phase").IsRequired()
.HasConversion(PhaseConverter).HasDefaultValue(PlanningPhase.None);
builder.Property(t => t.BlockedByTaskId).HasColumnName("blocked_by_task_id");
builder.Property(t => t.ScheduledFor).HasColumnName("scheduled_for");
builder.Property(t => t.Result).HasColumnName("result");
builder.Property(t => t.LogPath).HasColumnName("log_path");
builder.Property(t => t.CreatedAt).HasColumnName("created_at").IsRequired();
builder.Property(t => t.StartedAt).HasColumnName("started_at");
builder.Property(t => t.FinishedAt).HasColumnName("finished_at");
builder.Property(t => t.CommitType).HasColumnName("commit_type").IsRequired().HasDefaultValue("chore");
builder.Property(t => t.Model).HasColumnName("model");
builder.Property(t => t.SystemPrompt).HasColumnName("system_prompt");
builder.Property(t => t.AgentPath).HasColumnName("agent_path");
builder.Property(t => t.IsStarred).HasColumnName("is_starred").HasDefaultValue(false);
builder.Property(t => t.IsMyDay).HasColumnName("is_my_day").HasDefaultValue(false);
builder.Property(t => t.Notes).HasColumnName("notes");
builder.Property(t => t.SortOrder).HasColumnName("sort_order").IsRequired().HasDefaultValue(0);
builder.Property(t => t.ParentTaskId).HasColumnName("parent_task_id");
builder.Property(t => t.PlanningSessionId).HasColumnName("planning_session_id");
builder.Property(t => t.PlanningSessionToken).HasColumnName("planning_session_token");
builder.Property(t => t.PlanningFinalizedAt).HasColumnName("planning_finalized_at");
builder.Property(t => t.CreatedBy).HasColumnName("created_by");
builder.HasOne(t => t.Parent)
.WithMany(t => t.Children)
.HasForeignKey(t => t.ParentTaskId)
.OnDelete(DeleteBehavior.Restrict);
// BlockedBy: predecessor in a sequential chain. SetNull on delete so child becomes pickable.
builder.HasOne<TaskEntity>()
.WithMany()
.HasForeignKey(t => t.BlockedByTaskId)
.OnDelete(DeleteBehavior.SetNull);
builder.HasOne(t => t.List)
.WithMany(l => l.Tasks)
.HasForeignKey(t => t.ListId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(t => t.Worktree)
.WithOne(w => w.Task)
.HasForeignKey<WorktreeEntity>(w => w.TaskId);
builder.HasIndex(t => t.ListId).HasDatabaseName("idx_tasks_list_id");
builder.HasIndex(t => t.Status).HasDatabaseName("idx_tasks_status");
builder.HasIndex(t => new { t.ListId, t.SortOrder }).HasDatabaseName("idx_tasks_list_sort");
builder.HasIndex(t => t.ParentTaskId).HasDatabaseName("idx_tasks_parent_task_id");
builder.HasIndex(t => t.BlockedByTaskId).HasDatabaseName("idx_tasks_blocked_by");
}
}