Merge branch 'main' into feat/poweruse-list-handler

Kollision mit #264 (Handoff-Budget im Brief sichtbar machen): beide Seiten haben
HandoffRoundTracker, HandoffMcpTools und InteractiveLaunchSpecService angefasst.

Der Konflikt war inhaltlich, nicht nur textuell. #264 liest den Cap an zwei
Stellen aus der Konstante (GetStatus und das roundInfo-Rendering), waehrend
dieser Branch ihn zum Parameter aus app_settings gemacht hat. Ein reiner
Text-Merge haette kompiliert und trotzdem gelogen: Register haette den
konfigurierten Cap durchgesetzt, waehrend der Brief dem Modell weiter "2 Runden
uebrig" gemeldet haette, obwohl es 39 hat.

Aufgeloest so, dass der Cap eine einzige Quelle hat:
- GetStatus(taskId, maxHandoffs) nimmt den Cap als Parameter, mit demselben
  Clamp wie Register. Die Rechnerei liegt damit an einer Stelle.
- GetHandoffStatus liest das Setting und gibt es weiter.
- Das roundInfo-Rendering benutzt GetStatus statt eigener Arithmetik.

Zwei Tests nageln genau diesen Drift fest, damit er beim naechsten Merge nicht
unbemerkt zurueckkommt: einer auf GetHandoffStatus, einer auf den Brief.

MaxHandoffs -> DefaultMaxHandoffs in #264s vier Test-Referenzen; die laufen alle
mit Default-Settings, die Semantik bleibt.
This commit is contained in:
Mika Kuns
2026-09-02 15:48:43 +02:00
41 changed files with 2323 additions and 225 deletions
@@ -493,6 +493,24 @@ namespace ClaudeDo.Data.Migrations
b.ToTable("task_attachments", (string)null);
});
modelBuilder.Entity("ClaudeDo.Data.Models.TaskDependencyEntity", b =>
{
b.Property<string>("TaskId")
.HasColumnType("TEXT")
.HasColumnName("task_id");
b.Property<string>("DependsOnTaskId")
.HasColumnType("TEXT")
.HasColumnName("depends_on_task_id");
b.HasKey("TaskId", "DependsOnTaskId");
b.HasIndex("DependsOnTaskId")
.HasDatabaseName("idx_task_dependencies_depends_on");
b.ToTable("task_dependencies", (string)null);
});
modelBuilder.Entity("ClaudeDo.Data.Models.TaskEntity", b =>
{
b.Property<string>("Id")
@@ -522,10 +540,6 @@ namespace ClaudeDo.Data.Migrations
.HasColumnType("TEXT")
.HasColumnName("created_by");
b.Property<string>("DependsOnTaskId")
.HasColumnType("TEXT")
.HasColumnName("depends_on_task_id");
b.Property<string>("Description")
.HasColumnType("TEXT")
.HasColumnName("description");
@@ -693,9 +707,6 @@ namespace ClaudeDo.Data.Migrations
b.HasIndex("BlockedByTaskId")
.HasDatabaseName("idx_tasks_blocked_by");
b.HasIndex("DependsOnTaskId")
.HasDatabaseName("idx_tasks_depends_on");
b.HasIndex("ListId")
.HasDatabaseName("idx_tasks_list_id");
@@ -930,6 +941,21 @@ namespace ClaudeDo.Data.Migrations
b.Navigation("Task");
});
modelBuilder.Entity("ClaudeDo.Data.Models.TaskDependencyEntity", b =>
{
b.HasOne("ClaudeDo.Data.Models.TaskEntity", null)
.WithMany()
.HasForeignKey("DependsOnTaskId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ClaudeDo.Data.Models.TaskEntity", null)
.WithMany("Dependencies")
.HasForeignKey("TaskId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ClaudeDo.Data.Models.TaskEntity", b =>
{
b.HasOne("ClaudeDo.Data.Models.TaskEntity", null)
@@ -937,11 +963,6 @@ namespace ClaudeDo.Data.Migrations
.HasForeignKey("BlockedByTaskId")
.OnDelete(DeleteBehavior.SetNull);
b.HasOne("ClaudeDo.Data.Models.TaskEntity", null)
.WithMany()
.HasForeignKey("DependsOnTaskId")
.OnDelete(DeleteBehavior.SetNull);
b.HasOne("ClaudeDo.Data.Models.ListEntity", "List")
.WithMany("Tasks")
.HasForeignKey("ListId")
@@ -991,6 +1012,8 @@ namespace ClaudeDo.Data.Migrations
{
b.Navigation("Children");
b.Navigation("Dependencies");
b.Navigation("Runs");
b.Navigation("Subtasks");