From 00589d1523f5ac2c5ba5dee3860a71b07a74c954 Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Sun, 29 Mar 2026 05:48:15 +0100 Subject: [PATCH 1/5] Upgrade EF Core packages to 9.0.14 and fix remaining FluentAssertions rename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EF Core 8→9 must be done as a single coordinated bump: Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.Sqlite, and Microsoft.EntityFrameworkCore.Design all need to be at the same major version. The three separate Dependabot PRs (#494, #495, #496) each caused version conflicts or runtime failures when applied individually. Also fixes the remaining BeLessOrEqualTo → BeLessThanOrEqualTo rename missed in PR #504 (midRangePayload variable in OpsCliApiTests.cs had a different prefix so replace_all didn't catch it). --- .../Taskdeck.Infrastructure/Taskdeck.Infrastructure.csproj | 6 +++--- backend/tests/Taskdeck.Api.Tests/OpsCliApiTests.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/Taskdeck.Infrastructure/Taskdeck.Infrastructure.csproj b/backend/src/Taskdeck.Infrastructure/Taskdeck.Infrastructure.csproj index c7d2c7289..e743bfb95 100644 --- a/backend/src/Taskdeck.Infrastructure/Taskdeck.Infrastructure.csproj +++ b/backend/src/Taskdeck.Infrastructure/Taskdeck.Infrastructure.csproj @@ -18,9 +18,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/backend/tests/Taskdeck.Api.Tests/OpsCliApiTests.cs b/backend/tests/Taskdeck.Api.Tests/OpsCliApiTests.cs index b9561890c..f3b0bc8f4 100644 --- a/backend/tests/Taskdeck.Api.Tests/OpsCliApiTests.cs +++ b/backend/tests/Taskdeck.Api.Tests/OpsCliApiTests.cs @@ -155,7 +155,7 @@ public async Task RunCommand_ShouldFallbackCorrelationId_WhenRequestCorrelationH var midRangePayload = await midRangeResponse.Content.ReadFromJsonAsync(); midRangePayload.Should().NotBeNull(); midRangePayload!.CorrelationId.Should().NotBe(midRangeInvalidCorrelationId); - midRangePayload.CorrelationId.Length.Should().BeLessOrEqualTo(100); + midRangePayload.CorrelationId.Length.Should().BeLessThanOrEqualTo(100); midRangeResponse.Headers.TryGetValues("X-Request-Id", out var midRangeResponseRequestIds).Should().BeTrue(); midRangeResponseRequestIds!.Single().Should().Be(midRangePayload.CorrelationId); From 972b10a96bc33e51ed29e6307f369e4e738b3551 Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Sun, 29 Mar 2026 05:51:52 +0100 Subject: [PATCH 2/5] Fix KnowledgeChunkRepository: revert to extension method syntax for EF Core 9 In EF Core 9, RelationalQueryableExtensions.ExecuteDeleteAsync no longer accepts a CancellationToken, so the explicit 2-arg static call fails. Reverting to extension method syntax resolves unambiguously to EntityFrameworkQueryableExtensions (which does accept CancellationToken) since there is no ambiguity when all EF Core packages are at the same v9 version. --- .../Repositories/KnowledgeChunkRepository.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/Taskdeck.Infrastructure/Repositories/KnowledgeChunkRepository.cs b/backend/src/Taskdeck.Infrastructure/Repositories/KnowledgeChunkRepository.cs index dd14c589a..88d60f770 100644 --- a/backend/src/Taskdeck.Infrastructure/Repositories/KnowledgeChunkRepository.cs +++ b/backend/src/Taskdeck.Infrastructure/Repositories/KnowledgeChunkRepository.cs @@ -25,8 +25,8 @@ public async Task DeleteByDocumentIdAsync( Guid documentId, CancellationToken cancellationToken = default) { - await RelationalQueryableExtensions.ExecuteDeleteAsync( - _dbSet.Where(c => c.DocumentId == documentId), - cancellationToken); + await _dbSet + .Where(c => c.DocumentId == documentId) + .ExecuteDeleteAsync(cancellationToken); } } From 6b799b4a694bf49579464a9c5f09fa7ca3f79b93 Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Sun, 29 Mar 2026 20:27:21 +0100 Subject: [PATCH 3/5] Suppress all logging providers in CLI to fix CliJsonContractTests EF Core 9.0.14 emits warnings to stdout via the default console logging provider, breaking JSON parsing in callers. Replace the per-category filter with ClearProviders() so CLI stdout is always clean JSON. --- backend/src/Taskdeck.Cli/Program.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/Taskdeck.Cli/Program.cs b/backend/src/Taskdeck.Cli/Program.cs index 7fcfc148c..d33ae6b2f 100644 --- a/backend/src/Taskdeck.Cli/Program.cs +++ b/backend/src/Taskdeck.Cli/Program.cs @@ -9,7 +9,10 @@ using Taskdeck.Infrastructure.Persistence; var builder = Host.CreateApplicationBuilder(args); -builder.Logging.AddFilter("Microsoft.EntityFrameworkCore", LogLevel.Warning); + +// CLI stdout must be clean JSON. Remove all default logging providers so EF Core +// and framework diagnostics never corrupt JSON output parsed by callers. +builder.Logging.ClearProviders(); var fallbackConnectionString = Environment.GetEnvironmentVariable("TASKDECK_CONNECTION_STRING") ?? "Data Source=taskdeck.db"; From 526f1834bc751b91872488c5e4336cb227ca3934 Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Sun, 29 Mar 2026 20:39:25 +0100 Subject: [PATCH 4/5] Fix EF Core 9 PendingModelChangesWarning by regenerating snapshot EF Core 9 introduces a PendingModelChangesWarning that throws when the migration snapshot does not match the current model fingerprint format. Bump EF Tools to 9.0.14 and add an empty migration to regenerate the snapshot in the EF Core 9 format, clearing the false-positive warning. --- backend/src/Taskdeck.Api/Taskdeck.Api.csproj | 4 +- ...29193734_EfCore9SnapshotResync.Designer.cs | 1751 +++++++++++++++++ .../20260329193734_EfCore9SnapshotResync.cs | 22 + .../TaskdeckDbContextModelSnapshot.cs | 2 +- 4 files changed, 1776 insertions(+), 3 deletions(-) create mode 100644 backend/src/Taskdeck.Infrastructure/Migrations/20260329193734_EfCore9SnapshotResync.Designer.cs create mode 100644 backend/src/Taskdeck.Infrastructure/Migrations/20260329193734_EfCore9SnapshotResync.cs diff --git a/backend/src/Taskdeck.Api/Taskdeck.Api.csproj b/backend/src/Taskdeck.Api/Taskdeck.Api.csproj index c251a4cbf..7614f6b42 100644 --- a/backend/src/Taskdeck.Api/Taskdeck.Api.csproj +++ b/backend/src/Taskdeck.Api/Taskdeck.Api.csproj @@ -17,8 +17,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/backend/src/Taskdeck.Infrastructure/Migrations/20260329193734_EfCore9SnapshotResync.Designer.cs b/backend/src/Taskdeck.Infrastructure/Migrations/20260329193734_EfCore9SnapshotResync.Designer.cs new file mode 100644 index 000000000..96fafcb78 --- /dev/null +++ b/backend/src/Taskdeck.Infrastructure/Migrations/20260329193734_EfCore9SnapshotResync.Designer.cs @@ -0,0 +1,1751 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Taskdeck.Infrastructure.Persistence; + +#nullable disable + +namespace Taskdeck.Infrastructure.Migrations +{ + [DbContext(typeof(TaskdeckDbContext))] + [Migration("20260329193734_EfCore9SnapshotResync")] + partial class EfCore9SnapshotResync + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.14"); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AgentProfile", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("PolicyJson") + .IsRequired() + .HasMaxLength(8000) + .HasColumnType("TEXT"); + + b.Property("ScopeBoardId") + .HasColumnType("TEXT"); + + b.Property("ScopeType") + .HasColumnType("INTEGER"); + + b.Property("TemplateKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TemplateKey"); + + b.HasIndex("UserId"); + + b.ToTable("AgentProfiles", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AgentRun", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AgentProfileId") + .HasColumnType("TEXT"); + + b.Property("ApproxCostUsd") + .HasColumnType("decimal(10, 6)"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("CompletedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("FailureReason") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("Objective") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("ProposalId") + .HasColumnType("TEXT"); + + b.Property("StartedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StepsExecuted") + .HasColumnType("INTEGER"); + + b.Property("Summary") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("TokensUsed") + .HasColumnType("INTEGER"); + + b.Property("TriggerType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AgentProfileId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Status"); + + b.HasIndex("UserId"); + + b.ToTable("AgentRuns", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AgentRunEvent", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Payload") + .IsRequired() + .HasMaxLength(16000) + .HasColumnType("TEXT"); + + b.Property("RunId") + .HasColumnType("TEXT"); + + b.Property("SequenceNumber") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RunId", "SequenceNumber") + .IsUnique(); + + b.ToTable("AgentRunEvents", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.ArchiveItem", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ArchivedAt") + .HasColumnType("TEXT"); + + b.Property("ArchivedByUserId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EntityId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("RestoreStatus") + .HasColumnType("INTEGER"); + + b.Property("RestoredAt") + .HasColumnType("TEXT"); + + b.Property("RestoredByUserId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SnapshotJson") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ArchivedAt"); + + b.HasIndex("ArchivedByUserId"); + + b.HasIndex("BoardId"); + + b.HasIndex("RestoreStatus"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("ArchiveItems", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Action") + .HasColumnType("INTEGER"); + + b.Property("Changes") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EntityId") + .HasColumnType("TEXT"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Timestamp"); + + b.HasIndex("UserId"); + + b.HasIndex("EntityType", "EntityId"); + + b.ToTable("AuditLogs", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AutomationProposal", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AppliedAt") + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DecidedAt") + .HasColumnType("TEXT"); + + b.Property("DecidedByUserId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("DiffPreview") + .HasColumnType("TEXT"); + + b.Property("ExpiresAt") + .HasColumnType("TEXT"); + + b.Property("FailureReason") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("RequestedByUserId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("RiskLevel") + .HasColumnType("INTEGER"); + + b.Property("SourceReferenceId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SourceType") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("ValidationIssues") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoardId"); + + b.HasIndex("CorrelationId"); + + b.HasIndex("ExpiresAt"); + + b.HasIndex("RequestedByUserId"); + + b.HasIndex("Status"); + + b.ToTable("AutomationProposals", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AutomationProposalOperation", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ActionType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ExpectedVersion") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("IdempotencyKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ProposalId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Sequence") + .HasColumnType("INTEGER"); + + b.Property("TargetId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("TargetType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IdempotencyKey") + .IsUnique(); + + b.HasIndex("ProposalId"); + + b.HasIndex("ProposalId", "Sequence"); + + b.ToTable("AutomationProposalOperations", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Board", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("IsArchived") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("OwnerId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Boards", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.BoardAccess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("GrantedAt") + .HasColumnType("TEXT"); + + b.Property("GrantedBy") + .HasColumnType("TEXT"); + + b.Property("Role") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("BoardId", "UserId") + .IsUnique(); + + b.ToTable("BoardAccesses", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Card", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BlockReason") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("ColumnId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("DueDate") + .HasColumnType("TEXT"); + + b.Property("IsBlocked") + .HasColumnType("INTEGER"); + + b.Property("Position") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoardId"); + + b.HasIndex("ColumnId"); + + b.ToTable("Cards", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CardComment", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AuthorUserId") + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("CardId") + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeletedAt") + .HasColumnType("TEXT"); + + b.Property("EditedAt") + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .HasColumnType("INTEGER"); + + b.Property("ParentCommentId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AuthorUserId"); + + b.HasIndex("BoardId"); + + b.HasIndex("CardId"); + + b.HasIndex("ParentCommentId"); + + b.HasIndex("CardId", "CreatedAt"); + + b.ToTable("CardComments", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CardCommentMention", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CardCommentId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("MentionedUserId") + .HasColumnType("TEXT"); + + b.Property("MentionedUsername") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CardCommentId"); + + b.HasIndex("MentionedUserId"); + + b.HasIndex("CardCommentId", "MentionedUserId") + .IsUnique(); + + b.ToTable("CardCommentMentions", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CardLabel", b => + { + b.Property("CardId") + .HasColumnType("TEXT"); + + b.Property("LabelId") + .HasColumnType("TEXT"); + + b.HasKey("CardId", "LabelId"); + + b.HasIndex("LabelId"); + + b.ToTable("CardLabels", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.ChatMessage", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DegradedReason") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("MessageType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ProposalId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Role") + .HasColumnType("INTEGER"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("TokenUsage") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("ProposalId"); + + b.HasIndex("SessionId"); + + b.ToTable("ChatMessages", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.ChatSession", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoardId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Status"); + + b.HasIndex("UserId"); + + b.ToTable("ChatSessions", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Column", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Position") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("WipLimit") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("BoardId", "Position") + .IsUnique(); + + b.ToTable("Columns", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CommandRun", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CompletedAt") + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("ExitCode") + .HasColumnType("INTEGER"); + + b.Property("OutputPreview") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("RequestedByUserId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("StartedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TemplateName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Truncated") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CorrelationId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("RequestedByUserId"); + + b.HasIndex("Status"); + + b.ToTable("CommandRuns", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CommandRunLog", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CommandRunId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Level") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Metadata") + .HasColumnType("TEXT"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CommandRunId"); + + b.HasIndex("Level"); + + b.HasIndex("Timestamp"); + + b.ToTable("CommandRunLogs", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.KnowledgeChunk", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ChunkIndex") + .HasColumnType("INTEGER"); + + b.Property("Content") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DocumentId") + .HasColumnType("TEXT"); + + b.Property("Metadata") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId"); + + b.HasIndex("DocumentId", "ChunkIndex") + .IsUnique(); + + b.ToTable("KnowledgeChunks", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.KnowledgeDocument", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsArchived") + .HasColumnType("INTEGER"); + + b.Property("SourceType") + .HasColumnType("INTEGER"); + + b.Property("SourceUrl") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("Tags") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoardId"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "IsArchived"); + + b.ToTable("KnowledgeDocuments", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Label", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("ColorHex") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoardId"); + + b.ToTable("Labels", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.LlmRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ErrorMessage") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("Payload") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ProcessedAt") + .HasColumnType("TEXT"); + + b.Property("RequestType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoardId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Status"); + + b.HasIndex("UserId", "Status"); + + b.ToTable("LlmRequests", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.LlmUsageRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("InputTokens") + .HasColumnType("INTEGER"); + + b.Property("Model") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("OutputTokens") + .HasColumnType("INTEGER"); + + b.Property("Provider") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Surface") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("Surface", "CreatedAt"); + + b.HasIndex("UserId", "CreatedAt"); + + b.ToTable("LlmUsageRecords", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Notification", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("Cadence") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeduplicationKey") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("IsRead") + .HasColumnType("INTEGER"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("ReadAt") + .HasColumnType("TEXT"); + + b.Property("SourceEntityId") + .HasColumnType("TEXT"); + + b.Property("SourceEntityType") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoardId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "DeduplicationKey") + .IsUnique(); + + b.HasIndex("UserId", "IsRead"); + + b.ToTable("Notifications", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.NotificationPreference", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AssignmentDigestEnabled") + .HasColumnType("INTEGER"); + + b.Property("AssignmentImmediateEnabled") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("InAppChannelEnabled") + .HasColumnType("INTEGER"); + + b.Property("MentionDigestEnabled") + .HasColumnType("INTEGER"); + + b.Property("MentionImmediateEnabled") + .HasColumnType("INTEGER"); + + b.Property("ProposalOutcomeDigestEnabled") + .HasColumnType("INTEGER"); + + b.Property("ProposalOutcomeImmediateEnabled") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("NotificationPreferences", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.OutboundWebhookDelivery", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AttemptCount") + .HasColumnType("INTEGER"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeliveredAt") + .HasColumnType("TEXT"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("LastAttemptAt") + .HasColumnType("TEXT"); + + b.Property("LastErrorMessage") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("LastResponseStatusCode") + .HasColumnType("INTEGER"); + + b.Property("NextAttemptAt") + .HasColumnType("TEXT"); + + b.Property("Payload") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0); + + b.Property("SubscriptionId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("SubscriptionId"); + + b.HasIndex("Status", "NextAttemptAt"); + + b.ToTable("OutboundWebhookDeliveries", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.OutboundWebhookSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("BoardId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedByUserId") + .HasColumnType("TEXT"); + + b.Property("EndpointUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("EventFilters") + .IsRequired() + .HasMaxLength(400) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("LastTriggeredAt") + .HasColumnType("TEXT"); + + b.Property("RevokedAt") + .HasColumnType("TEXT"); + + b.Property("RevokedByUserId") + .HasColumnType("TEXT"); + + b.Property("SigningSecret") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("BoardId", "IsActive"); + + b.ToTable("OutboundWebhookSubscriptions", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DefaultRole") + .HasColumnType("INTEGER"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("PasswordHash") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.UserPreference", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("OnboardingCompletedAt") + .HasColumnType("TEXT"); + + b.Property("OnboardingDismissedAt") + .HasColumnType("TEXT"); + + b.Property("OnboardingVisibility") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("WorkspaceMode") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("UserPreferences", (string)null); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AgentRun", b => + { + b.HasOne("Taskdeck.Domain.Entities.AgentProfile", null) + .WithMany() + .HasForeignKey("AgentProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AgentRunEvent", b => + { + b.HasOne("Taskdeck.Domain.Entities.AgentRun", "Run") + .WithMany("Events") + .HasForeignKey("RunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AuditLog", b => + { + b.HasOne("Taskdeck.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AutomationProposalOperation", b => + { + b.HasOne("Taskdeck.Domain.Entities.AutomationProposal", "Proposal") + .WithMany("Operations") + .HasForeignKey("ProposalId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Proposal"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Board", b => + { + b.HasOne("Taskdeck.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.BoardAccess", b => + { + b.HasOne("Taskdeck.Domain.Entities.Board", "Board") + .WithMany("BoardAccesses") + .HasForeignKey("BoardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Taskdeck.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Board"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Card", b => + { + b.HasOne("Taskdeck.Domain.Entities.Board", "Board") + .WithMany("Cards") + .HasForeignKey("BoardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Taskdeck.Domain.Entities.Column", "Column") + .WithMany("Cards") + .HasForeignKey("ColumnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Board"); + + b.Navigation("Column"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CardComment", b => + { + b.HasOne("Taskdeck.Domain.Entities.User", "AuthorUser") + .WithMany() + .HasForeignKey("AuthorUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Taskdeck.Domain.Entities.Card", "Card") + .WithMany() + .HasForeignKey("CardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Taskdeck.Domain.Entities.CardComment", "ParentComment") + .WithMany("Replies") + .HasForeignKey("ParentCommentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("AuthorUser"); + + b.Navigation("Card"); + + b.Navigation("ParentComment"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CardCommentMention", b => + { + b.HasOne("Taskdeck.Domain.Entities.CardComment", "CardComment") + .WithMany("Mentions") + .HasForeignKey("CardCommentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Taskdeck.Domain.Entities.User", "MentionedUser") + .WithMany() + .HasForeignKey("MentionedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CardComment"); + + b.Navigation("MentionedUser"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CardLabel", b => + { + b.HasOne("Taskdeck.Domain.Entities.Card", "Card") + .WithMany("CardLabels") + .HasForeignKey("CardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Taskdeck.Domain.Entities.Label", "Label") + .WithMany("CardLabels") + .HasForeignKey("LabelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Card"); + + b.Navigation("Label"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.ChatMessage", b => + { + b.HasOne("Taskdeck.Domain.Entities.ChatSession", "Session") + .WithMany("Messages") + .HasForeignKey("SessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Session"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Column", b => + { + b.HasOne("Taskdeck.Domain.Entities.Board", "Board") + .WithMany("Columns") + .HasForeignKey("BoardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Board"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CommandRunLog", b => + { + b.HasOne("Taskdeck.Domain.Entities.CommandRun", "CommandRun") + .WithMany("Logs") + .HasForeignKey("CommandRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CommandRun"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.KnowledgeChunk", b => + { + b.HasOne("Taskdeck.Domain.Entities.KnowledgeDocument", null) + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Label", b => + { + b.HasOne("Taskdeck.Domain.Entities.Board", "Board") + .WithMany("Labels") + .HasForeignKey("BoardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Board"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.LlmRequest", b => + { + b.HasOne("Taskdeck.Domain.Entities.Board", "Board") + .WithMany() + .HasForeignKey("BoardId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Taskdeck.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Board"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Notification", b => + { + b.HasOne("Taskdeck.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.NotificationPreference", b => + { + b.HasOne("Taskdeck.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.OutboundWebhookDelivery", b => + { + b.HasOne("Taskdeck.Domain.Entities.OutboundWebhookSubscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.OutboundWebhookSubscription", b => + { + b.HasOne("Taskdeck.Domain.Entities.Board", "Board") + .WithMany() + .HasForeignKey("BoardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Taskdeck.Domain.Entities.User", "CreatedByUser") + .WithMany() + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Board"); + + b.Navigation("CreatedByUser"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.UserPreference", b => + { + b.HasOne("Taskdeck.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AgentRun", b => + { + b.Navigation("Events"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.AutomationProposal", b => + { + b.Navigation("Operations"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Board", b => + { + b.Navigation("BoardAccesses"); + + b.Navigation("Cards"); + + b.Navigation("Columns"); + + b.Navigation("Labels"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Card", b => + { + b.Navigation("CardLabels"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CardComment", b => + { + b.Navigation("Mentions"); + + b.Navigation("Replies"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.ChatSession", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Column", b => + { + b.Navigation("Cards"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.CommandRun", b => + { + b.Navigation("Logs"); + }); + + modelBuilder.Entity("Taskdeck.Domain.Entities.Label", b => + { + b.Navigation("CardLabels"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Taskdeck.Infrastructure/Migrations/20260329193734_EfCore9SnapshotResync.cs b/backend/src/Taskdeck.Infrastructure/Migrations/20260329193734_EfCore9SnapshotResync.cs new file mode 100644 index 000000000..5201941c1 --- /dev/null +++ b/backend/src/Taskdeck.Infrastructure/Migrations/20260329193734_EfCore9SnapshotResync.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Taskdeck.Infrastructure.Migrations +{ + /// + public partial class EfCore9SnapshotResync : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/backend/src/Taskdeck.Infrastructure/Migrations/TaskdeckDbContextModelSnapshot.cs b/backend/src/Taskdeck.Infrastructure/Migrations/TaskdeckDbContextModelSnapshot.cs index 357eb20a4..020880153 100644 --- a/backend/src/Taskdeck.Infrastructure/Migrations/TaskdeckDbContextModelSnapshot.cs +++ b/backend/src/Taskdeck.Infrastructure/Migrations/TaskdeckDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ partial class TaskdeckDbContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + modelBuilder.HasAnnotation("ProductVersion", "9.0.14"); modelBuilder.Entity("Taskdeck.Domain.Entities.AgentProfile", b => { From 48cd087b9f85125902f42523ddfe04a12f5b1df4 Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Sun, 29 Mar 2026 20:49:15 +0100 Subject: [PATCH 5/5] Suppress PendingModelChangesWarning in DbContext after EF Core 9 upgrade EF Core 9 throws PendingModelChangesWarning during Migrate() when the snapshot fingerprint format changed from EF Core 8. EF tooling confirms no actual model changes are pending. Suppress the warning per the EF Core docs recommendation to prevent startup crashes. --- .../src/Taskdeck.Infrastructure/DependencyInjection.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/src/Taskdeck.Infrastructure/DependencyInjection.cs b/backend/src/Taskdeck.Infrastructure/DependencyInjection.cs index 7ce55127d..ffc865841 100644 --- a/backend/src/Taskdeck.Infrastructure/DependencyInjection.cs +++ b/backend/src/Taskdeck.Infrastructure/DependencyInjection.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Taskdeck.Application.Interfaces; @@ -16,7 +17,12 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi ?? "Data Source=taskdeck.db"; services.AddDbContext(options => - options.UseSqlite(connectionString)); + options + .UseSqlite(connectionString) + // EF Core 9 introduced PendingModelChangesWarning which throws when upgrading + // from EF Core 8 snapshots. EF tooling confirms no actual model changes are + // pending; suppress to allow startup after the snapshot format migration. + .ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning))); services.AddScoped(); services.AddScoped();