Runaway sessions were the single biggest cost driver: model_presets was never persisted (stayed code-only), default_max_turns shipped at 100, and ResolveMaxTurns had no upper bound, so a task/list override could run hundreds of turns unchecked. - TaskRunner.ResolveMaxTurns now clamps the resolved value to AppSettings.MaxTurnsCeiling (new column, default 80) and logs a warning with task id / requested / effective value when it clamps. - default_max_turns default lowered from 100 to 40 (entity, EF config, and the seeded row via the new AddMaxTurnsCeiling migration). - AppSettingsRepository.GetAsync backfills model_presets with the shipping defaults on first read instead of leaving the column null. - Settings > General's per-model preset table and the task/list agent editor now show a hint when a set max-turns value exceeds the ceiling.
28 lines
1.0 KiB
C#
28 lines
1.0 KiB
C#
using ClaudeDo.Worker.Runner;
|
|
using Xunit;
|
|
|
|
namespace ClaudeDo.Worker.Tests.Runner;
|
|
|
|
public class MaxTurnsResolutionTests
|
|
{
|
|
[Fact]
|
|
public void Task_override_wins()
|
|
=> Assert.Equal(5, TaskRunner.ResolveMaxTurns(taskTurns: 5, listTurns: 20, globalDefault: 100, ceiling: 200));
|
|
|
|
[Fact]
|
|
public void List_override_used_when_no_task_override()
|
|
=> Assert.Equal(20, TaskRunner.ResolveMaxTurns(taskTurns: null, listTurns: 20, globalDefault: 100, ceiling: 200));
|
|
|
|
[Fact]
|
|
public void Falls_back_to_global_default()
|
|
=> Assert.Equal(100, TaskRunner.ResolveMaxTurns(taskTurns: null, listTurns: null, globalDefault: 100, ceiling: 200));
|
|
|
|
[Fact]
|
|
public void Below_ceiling_is_unchanged()
|
|
=> Assert.Equal(60, TaskRunner.ResolveMaxTurns(taskTurns: 60, listTurns: null, globalDefault: 40, ceiling: 80));
|
|
|
|
[Fact]
|
|
public void Above_ceiling_is_clamped()
|
|
=> Assert.Equal(80, TaskRunner.ResolveMaxTurns(taskTurns: 200, listTurns: null, globalDefault: 40, ceiling: 80));
|
|
}
|