diff --git a/src/ClaudeDo.Data/Repositories/ListRepository.cs b/src/ClaudeDo.Data/Repositories/ListRepository.cs index 99054db..8346875 100644 --- a/src/ClaudeDo.Data/Repositories/ListRepository.cs +++ b/src/ClaudeDo.Data/Repositories/ListRepository.cs @@ -65,6 +65,7 @@ public sealed class ListRepository existing.Model = config.Model; existing.SystemPrompt = config.SystemPrompt; existing.AgentPath = config.AgentPath; + existing.MaxTurns = config.MaxTurns; } await _context.SaveChangesAsync(ct); } diff --git a/src/ClaudeDo.Data/Repositories/TaskRepository.cs b/src/ClaudeDo.Data/Repositories/TaskRepository.cs index 8ef880d..88d45be 100644 --- a/src/ClaudeDo.Data/Repositories/TaskRepository.cs +++ b/src/ClaudeDo.Data/Repositories/TaskRepository.cs @@ -159,6 +159,7 @@ public sealed class TaskRepository string? model, string? systemPrompt, string? agentPath, + int? maxTurns = null, CancellationToken ct = default) { await _context.Tasks @@ -166,7 +167,8 @@ public sealed class TaskRepository .ExecuteUpdateAsync(s => s .SetProperty(t => t.Model, model) .SetProperty(t => t.SystemPrompt, systemPrompt) - .SetProperty(t => t.AgentPath, agentPath), ct); + .SetProperty(t => t.AgentPath, agentPath) + .SetProperty(t => t.MaxTurns, maxTurns), ct); } #endregion diff --git a/src/ClaudeDo.Worker/External/ConfigMcpTools.cs b/src/ClaudeDo.Worker/External/ConfigMcpTools.cs index 0024e4a..ab4d872 100644 --- a/src/ClaudeDo.Worker/External/ConfigMcpTools.cs +++ b/src/ClaudeDo.Worker/External/ConfigMcpTools.cs @@ -58,7 +58,7 @@ public sealed class ConfigMcpTools _ = await _tasks.GetByIdAsync(taskId, cancellationToken) ?? throw new InvalidOperationException($"Task {taskId} not found."); - await _tasks.UpdateAgentSettingsAsync(taskId, model.NullIfBlank(), systemPrompt.NullIfBlank(), agentPath.NullIfBlank(), cancellationToken); + await _tasks.UpdateAgentSettingsAsync(taskId, model.NullIfBlank(), systemPrompt.NullIfBlank(), agentPath.NullIfBlank(), ct: cancellationToken); await _broadcaster.TaskUpdated(taskId); } } diff --git a/tests/ClaudeDo.Worker.Tests/Repositories/MaxTurnsRoundTripTests.cs b/tests/ClaudeDo.Worker.Tests/Repositories/MaxTurnsRoundTripTests.cs new file mode 100644 index 0000000..ff0b55e --- /dev/null +++ b/tests/ClaudeDo.Worker.Tests/Repositories/MaxTurnsRoundTripTests.cs @@ -0,0 +1,79 @@ +using ClaudeDo.Data; +using ClaudeDo.Data.Models; +using ClaudeDo.Data.Repositories; +using ClaudeDo.Worker.Tests.Infrastructure; + +namespace ClaudeDo.Worker.Tests.Repositories; + +public sealed class MaxTurnsRoundTripTests : IDisposable +{ + private readonly DbFixture _db = new(); + + public void Dispose() => _db.Dispose(); + + [Fact] + public async Task ListConfig_persists_max_turns() + { + var listId = Guid.NewGuid().ToString(); + using (var ctx = _db.CreateContext()) + { + await new ListRepository(ctx).AddAsync(new ListEntity + { + Id = listId, Name = "L", CreatedAt = DateTime.UtcNow, + }); + } + + using (var ctx = _db.CreateContext()) + { + await new ListRepository(ctx).SetConfigAsync(new ListConfigEntity + { + ListId = listId, MaxTurns = 42, + }); + } + + using var readCtx = _db.CreateContext(); + var config = await new ListRepository(readCtx).GetConfigAsync(listId); + Assert.NotNull(config); + Assert.Equal(42, config!.MaxTurns); + } + + [Fact] + public async Task Task_agent_settings_persist_and_clear_max_turns() + { + var listId = Guid.NewGuid().ToString(); + var taskId = Guid.NewGuid().ToString(); + using (var ctx = _db.CreateContext()) + { + await new ListRepository(ctx).AddAsync(new ListEntity + { + Id = listId, Name = "L", CreatedAt = DateTime.UtcNow, + }); + await new TaskRepository(ctx).AddAsync(new TaskEntity + { + Id = taskId, ListId = listId, Title = "t", CreatedAt = DateTime.UtcNow, + }); + } + + using (var ctx = _db.CreateContext()) + { + await new TaskRepository(ctx).UpdateAgentSettingsAsync(taskId, null, null, null, maxTurns: 7); + } + + using (var ctx = _db.CreateContext()) + { + var entity = await new TaskRepository(ctx).GetByIdAsync(taskId); + Assert.NotNull(entity); + Assert.Equal(7, entity!.MaxTurns); + } + + using (var ctx = _db.CreateContext()) + { + await new TaskRepository(ctx).UpdateAgentSettingsAsync(taskId, null, null, null, maxTurns: null); + } + + using var readCtx = _db.CreateContext(); + var final = await new TaskRepository(readCtx).GetByIdAsync(taskId); + Assert.NotNull(final); + Assert.Null(final!.MaxTurns); + } +}