diff --git a/src/ClaudeDo.Worker/Hub/WorkerHub.cs b/src/ClaudeDo.Worker/Hub/WorkerHub.cs index 5f8b24f0..a5028c38 100644 --- a/src/ClaudeDo.Worker/Hub/WorkerHub.cs +++ b/src/ClaudeDo.Worker/Hub/WorkerHub.cs @@ -1394,6 +1394,11 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub try { var result = await _ticketImport.ImportAsync(listId, Context.ConnectionAborted); + // ListUpdated allein reicht nicht: die UI liest das als "Listen-Metadaten geaendert" + // und patcht nur bestehende Zeilen. Neue Tasks tauchen nur ueber TaskUpdated auf, wie + // bei jedem anderen out-of-band Creator (add_task und Co.). + foreach (var taskId in result.CreatedTaskIds) + await _broadcaster.TaskUpdated(taskId); await _broadcaster.ListUpdated(listId); return new TicketImportResultDto(result.Examined, result.Created); } diff --git a/src/ClaudeDo.Worker/Tickets/TicketImportService.cs b/src/ClaudeDo.Worker/Tickets/TicketImportService.cs index 985ffc73..d7e9c818 100644 --- a/src/ClaudeDo.Worker/Tickets/TicketImportService.cs +++ b/src/ClaudeDo.Worker/Tickets/TicketImportService.cs @@ -6,7 +6,7 @@ using TaskStatus = ClaudeDo.Data.Models.TaskStatus; namespace ClaudeDo.Worker.Tickets; -public sealed record TicketImportResult(int Examined, int Created); +public sealed record TicketImportResult(int Examined, int Created, IReadOnlyList CreatedTaskIds); /// /// Holt das Board des verknüpften Ticket-Projekts und legt für jedes offene, dem Token-Inhaber @@ -61,16 +61,17 @@ public sealed class TicketImportService var knownSet = known.ToHashSet(StringComparer.OrdinalIgnoreCase); var repo = new TaskRepository(ctx); - var created = 0; + var created = new List(); foreach (var ticket in mine) { var reference = $"bandel:{ticket.Id}"; if (!knownSet.Add(reference)) continue; + var taskId = Guid.NewGuid().ToString(); await repo.AddAsync(new TaskEntity { - Id = Guid.NewGuid().ToString(), + Id = taskId, ListId = listId, Title = ticket.Title, Description = BuildDescription(ticket), @@ -79,14 +80,14 @@ public sealed class TicketImportService TicketRef = reference, CreatedBy = "ticketsystem", }, ct); - created++; + created.Add(taskId); } _logger.LogInformation( "Ticket-Import für Liste {list_id}: {examined} geprüft, {created} neue Tasks", - listId, board.Count, created); + listId, board.Count, created.Count); - return new TicketImportResult(board.Count, created); + return new TicketImportResult(board.Count, created.Count, created); } /// diff --git a/tests/ClaudeDo.Worker.Tests/Tickets/TicketImportServiceTests.cs b/tests/ClaudeDo.Worker.Tests/Tickets/TicketImportServiceTests.cs index 08b390b2..6a61547a 100644 --- a/tests/ClaudeDo.Worker.Tests/Tickets/TicketImportServiceTests.cs +++ b/tests/ClaudeDo.Worker.Tests/Tickets/TicketImportServiceTests.cs @@ -43,6 +43,14 @@ public sealed class TicketImportServiceTests : IDisposable .ToListAsync(); Assert.Equal(["bandel:1", "bandel:3"], refs.Order()); + // Ohne die Ids kann der Hub kein TaskUpdated broadcasten und die offene Liste + // zeigt die neuen Tasks erst nach Schließen/Öffnen. + var createdIds = await ctx.Tasks + .Where(t => t.CreatedBy == "ticketsystem") + .Select(t => t.Id) + .ToListAsync(); + Assert.Equal(createdIds.Order(), result.CreatedTaskIds.Order()); + var created = await ctx.Tasks.Where(t => t.TicketRef == "bandel:1").SingleAsync(); Assert.Equal("Fix X", created.Title); Assert.Equal(TaskStatus.Idle, created.Status);