feat(data): persist max_turns in list and task repositories

Add MaxTurns to ListRepository.SetConfigAsync upsert branch and
TaskRepository.UpdateAgentSettingsAsync; fix positional CancellationToken
call in ConfigMcpTools. Covered by MaxTurnsRoundTripTests (2 tests).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-06-04 12:18:32 +02:00
parent 97e38fb480
commit ac137f7c1c
4 changed files with 84 additions and 2 deletions

View File

@@ -65,6 +65,7 @@ public sealed class ListRepository
existing.Model = config.Model; existing.Model = config.Model;
existing.SystemPrompt = config.SystemPrompt; existing.SystemPrompt = config.SystemPrompt;
existing.AgentPath = config.AgentPath; existing.AgentPath = config.AgentPath;
existing.MaxTurns = config.MaxTurns;
} }
await _context.SaveChangesAsync(ct); await _context.SaveChangesAsync(ct);
} }

View File

@@ -159,6 +159,7 @@ public sealed class TaskRepository
string? model, string? model,
string? systemPrompt, string? systemPrompt,
string? agentPath, string? agentPath,
int? maxTurns = null,
CancellationToken ct = default) CancellationToken ct = default)
{ {
await _context.Tasks await _context.Tasks
@@ -166,7 +167,8 @@ public sealed class TaskRepository
.ExecuteUpdateAsync(s => s .ExecuteUpdateAsync(s => s
.SetProperty(t => t.Model, model) .SetProperty(t => t.Model, model)
.SetProperty(t => t.SystemPrompt, systemPrompt) .SetProperty(t => t.SystemPrompt, systemPrompt)
.SetProperty(t => t.AgentPath, agentPath), ct); .SetProperty(t => t.AgentPath, agentPath)
.SetProperty(t => t.MaxTurns, maxTurns), ct);
} }
#endregion #endregion

View File

@@ -58,7 +58,7 @@ public sealed class ConfigMcpTools
_ = await _tasks.GetByIdAsync(taskId, cancellationToken) _ = await _tasks.GetByIdAsync(taskId, cancellationToken)
?? throw new InvalidOperationException($"Task {taskId} not found."); ?? 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); await _broadcaster.TaskUpdated(taskId);
} }
} }

View File

@@ -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);
}
}