feat(data): PowerUse-Flags und konfigurierbares Handoff-Budget

app_settings bekommt power_use_enabled (Master-Schalter, wird im Usage-Monitor
gesetzt) und list_handler_max_handoffs (Default 4 = zwei Merge-Runden).
list_config bekommt power_use_allowed als Opt-in pro Liste.

Ein Handler-Run laeuft nur unbeaufsichtigt, wenn BEIDE Schalter gesetzt sind.
Damit kann der globale Schalter ueber Nacht an bleiben, ohne dass der Handler
einer nicht zugestimmten Liste sein Verhalten aendert.

UpdateListConfigDto.PowerUseAllowed ist bewusst bool? (tri-state, null =
gespeicherten Wert behalten), analog zu TicketProjectId: SetConfigAsync kopiert
verbatim, ein non-nullable bool haette jedem fremden Writer erlaubt, das Opt-in
per Auslassung zu widerrufen. Der Test dazu nagelt genau das fest.

Migration ist drei reine ALTER TABLE ADD COLUMN, kein Table-Rebuild.
This commit is contained in:
Mika Kuns
2026-09-02 15:43:32 +02:00
parent 20685827fb
commit 3cf9628f46
11 changed files with 1217 additions and 3 deletions
@@ -60,4 +60,17 @@ public sealed class AppSettingsEntity
// automatic ContinueTask once the 5h window resets, and the gate-blocked queue wakes exactly
// at that reset time instead of only on the 30s backstop.
public bool AutoContinueOnUsageLimit { get; set; }
// Master switch for PowerUse: list-handler runs go unattended (never ask the user) and harvest
// new work when a round would otherwise starve. Toggled from the usage monitor, because it is a
// deliberate "spend the remaining window" decision rather than a normal default. Off by default,
// and on its own it changes nothing — a run is only in PowerUse when the LIST also opted in via
// ListConfigEntity.PowerUseAllowed, so leaving this on cannot make an unrelated list's handler
// run unattended. Composition lives in InteractiveLaunchSpecService.
public bool PowerUseEnabled { get; set; }
// Server-side cap on a list-handler run's wait<->merge handoff chain (HandoffRoundTracker).
// 4 = two full wait/merge cycles, the long-standing default. Raise it for a run meant to keep
// going for hours. It exists to bound runaway handoff loops, so it must stay >= 1.
public int ListHandlerMaxHandoffs { get; set; } = 4;
}