feat(worker): Toggle "Continue on session limit reset" für Usage-Limit-Abbrüche
Klassifiziert einen echten Usage-Limit-Abbruch als eigene FailureReason
"usage_limit" (TaskRunner.ClassifyFailureReason: nur bei terminal_reason
"api_error" plus einem Limit-Muster im gerenderten Fehlertext, nicht an
Status==Failed allein). Neuer Toggle AutoContinueOnUsageLimit (app_settings,
Default aus) unter Settings → General → "Usage limit stop":
- UsageLimitAutoContinueCoordinator feuert pro Task genau einmal ContinueTask
über OverrideSlotService, sobald das 5h-Fenster (UsageState.Snapshot.FiveHour
.ResetsAt) tatsächlich zurückgesetzt ist; ein persistenter Marker
(TaskEntity.UsageLimitAutoContinuedAt) verhindert einen zweiten Anlauf bei
einem erneuten Limit-Treffer.
- QueueService schedult zusätzlich einen exakten Wake-Timer auf den
Reset-Zeitpunkt, statt nur auf den 30s-Backstop zu warten.
- Fail-open durchgängig: kein Snapshot/keine Reset-Zeit → kein Timer, kein
Continue, kein Throw. Toggle aus ändert das heutige Verhalten nicht.
Migration AddUsageLimitAutoContinue fügt beide Spalten hinzu; die von
`dotnet ef migrations add` mitgescaffoldete leere UpdateData auf app_settings
(columns/values: []) erzeugte ungültiges SQL ("near WHERE") und wurde entfernt
— TaskNumberMigrationTests deckte das über den vollen Migrate()-Pfad auf.
This commit is contained in:
@@ -77,8 +77,10 @@ public sealed class AddSubtaskToolTests : IDisposable
|
||||
var picker = new ClaudeDo.Worker.Queue.QueuePicker(dbFactory);
|
||||
var runCancels = new RunCancellationRegistry(NullLogger<RunCancellationRegistry>.Instance);
|
||||
var overrideSlot = new OverrideSlotService(dbFactory, runner, NullLogger<OverrideSlotService>.Instance, runCancels);
|
||||
var usageState = new UsageState();
|
||||
var queue = new QueueService(dbFactory, runner, cfg, NullLogger<QueueService>.Instance, waker, picker, overrideSlot, state, runCancels,
|
||||
new FakeUsageGate(), new UsageState(), broadcaster);
|
||||
new FakeUsageGate(), usageState, broadcaster,
|
||||
new UsageLimitAutoContinueCoordinator(dbFactory, usageState, overrideSlot, broadcaster, NullLogger<UsageLimitAutoContinueCoordinator>.Instance));
|
||||
var maintenance = new WorktreeMaintenanceService(dbFactory, git, NullLogger<WorktreeMaintenanceService>.Instance);
|
||||
var merge = new TaskMergeService(dbFactory, git, broadcaster, state, new VerifyCommandRunner(), NullLogger<TaskMergeService>.Instance);
|
||||
var aggregator = new PlanningAggregator(dbFactory, git, NullLogger<PlanningAggregator>.Instance);
|
||||
|
||||
@@ -111,9 +111,11 @@ public sealed class BatchMcpToolsTests : IDisposable
|
||||
NullLogger<TaskRunner>.Instance, state, new TaskRunTokenRegistry(), new AttachmentStore(), new FakeSessionSkillSeeder(), new FakeTranscriptUsageReader());
|
||||
var runCancels = new RunCancellationRegistry(NullLogger<RunCancellationRegistry>.Instance);
|
||||
var overrideSlot = new OverrideSlotService(dbFactory, runner, NullLogger<OverrideSlotService>.Instance, runCancels);
|
||||
var usageState = new UsageState();
|
||||
return new QueueService(dbFactory, runner, cfg, NullLogger<QueueService>.Instance,
|
||||
new QueueWaker(), new QueuePicker(dbFactory), overrideSlot, state, runCancels,
|
||||
new FakeUsageGate(), new UsageState(), broadcaster);
|
||||
new FakeUsageGate(), usageState, broadcaster,
|
||||
new UsageLimitAutoContinueCoordinator(dbFactory, usageState, overrideSlot, broadcaster, NullLogger<UsageLimitAutoContinueCoordinator>.Instance));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -178,8 +178,10 @@ public sealed class ExternalMcpServiceTests : IDisposable
|
||||
var picker = new ClaudeDo.Worker.Queue.QueuePicker(dbFactory);
|
||||
var runCancels = new RunCancellationRegistry(NullLogger<RunCancellationRegistry>.Instance);
|
||||
var overrideSlot = new OverrideSlotService(dbFactory, runner, NullLogger<OverrideSlotService>.Instance, runCancels);
|
||||
var usageState = new UsageState();
|
||||
return new QueueService(dbFactory, runner, cfg, NullLogger<QueueService>.Instance, waker, picker, overrideSlot, state, runCancels,
|
||||
new FakeUsageGate(), new UsageState(), broadcaster);
|
||||
new FakeUsageGate(), usageState, broadcaster,
|
||||
new UsageLimitAutoContinueCoordinator(dbFactory, usageState, overrideSlot, broadcaster, NullLogger<UsageLimitAutoContinueCoordinator>.Instance));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -83,9 +83,11 @@ public sealed class LifecycleMcpToolsTests : IDisposable
|
||||
NullLogger<TaskRunner>.Instance, state, new TaskRunTokenRegistry(), new AttachmentStore(), new FakeSessionSkillSeeder(), new FakeTranscriptUsageReader());
|
||||
var runCancels = new RunCancellationRegistry(NullLogger<RunCancellationRegistry>.Instance);
|
||||
var overrideSlot = new OverrideSlotService(dbFactory, runner, NullLogger<OverrideSlotService>.Instance, runCancels);
|
||||
var usageState = new UsageState();
|
||||
return new QueueService(dbFactory, runner, cfg, NullLogger<QueueService>.Instance,
|
||||
new QueueWaker(), new QueuePicker(dbFactory), overrideSlot, state, runCancels,
|
||||
new FakeUsageGate(), new UsageState(), broadcaster);
|
||||
new FakeUsageGate(), usageState, broadcaster,
|
||||
new UsageLimitAutoContinueCoordinator(dbFactory, usageState, overrideSlot, broadcaster, NullLogger<UsageLimitAutoContinueCoordinator>.Instance));
|
||||
}
|
||||
|
||||
private async Task<TaskEntity> SeedTaskAsync(
|
||||
|
||||
@@ -60,8 +60,10 @@ public sealed class QueueStateMcpToolsTests : IDisposable
|
||||
new FakeSessionSkillSeeder(), new FakeTranscriptUsageReader());
|
||||
var picker = new QueuePicker(dbFactory);
|
||||
var overrideSlot = new OverrideSlotService(dbFactory, runner, NullLogger<OverrideSlotService>.Instance, built.RunCancels);
|
||||
var resolvedUsageState = usageState ?? new UsageState();
|
||||
var queue = new QueueService(dbFactory, runner, _cfg, NullLogger<QueueService>.Instance, new QueueWaker(), picker,
|
||||
overrideSlot, built.State, built.RunCancels, new FakeUsageGate(), usageState ?? new UsageState(), broadcaster);
|
||||
overrideSlot, built.State, built.RunCancels, new FakeUsageGate(), resolvedUsageState, broadcaster,
|
||||
new UsageLimitAutoContinueCoordinator(dbFactory, resolvedUsageState, overrideSlot, broadcaster, NullLogger<UsageLimitAutoContinueCoordinator>.Instance));
|
||||
return (queue, new QueueStateMcpTools(queue, dbFactory));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user