Merge branch 'claudedo/2de2f008758640b3a75e95719b1555bf'

This commit is contained in:
mika kuns
2026-08-05 16:07:10 +02:00
22 changed files with 1093 additions and 50 deletions
+1 -1
View File
@@ -161,7 +161,7 @@ A child that hits a roadblock (fails, or reports `CLAUDEDO_BLOCKED` roadblocks)
1. Load task + list metadata from DB; resolve config from `list_config` + task-level overrides (model, system_prompt, agent_path)
2. Create worktree (if `WorkingDir` set) or sandbox directory
3. Mark task "running", broadcast `TaskStarted`
4. Resolve the effective model (task → list → `AppSettings.DefaultModel`), then take its `ModelPresets` row via `ModelPresets.For(global.ModelPresets, model, global.DefaultMaxTurns)`: the model string is resolved through `ModelRegistry.TryNormalizeAlias` first, so a full CLI model id (e.g. `claude-sonnet-4-6`, not just the bare `sonnet`/`opus`/`haiku`/`fable` aliases) still hits its alias's preset row; only a truly unrecognized model falls back to a synthesized row using `AppSettings.DefaultMaxTurns` (never a hardcoded number, and never throws — an unknown model must not block a run). The preset supplies `--effort` and the **global** max-turns default (task/list `MaxTurns` overrides still win). Build CLI args via `ClaudeArgsBuilder`; inject attachment absolute paths via `TaskPromptComposer.Compose` (appends a read-only "## Reference files" section); invoke `ClaudeProcess` with task prompt
4. Resolve the effective model (task → list → `AppSettings.DefaultModel`), then take its `ModelPresets` row via `ModelPresets.For(global.ModelPresets, model, global.DefaultMaxTurns)`: the model string is resolved through `ModelRegistry.TryNormalizeAlias` first, so a full CLI model id (e.g. `claude-sonnet-4-6`, not just the bare `sonnet`/`opus`/`haiku`/`fable` aliases) still hits its alias's preset row; only a truly unrecognized model falls back to a synthesized row using `AppSettings.DefaultMaxTurns` (never a hardcoded number, and never throws — an unknown model must not block a run). The preset supplies `--effort` and the **global** max-turns default (task/list `MaxTurns` overrides still win). The task/list/global-resolved value is then hard-clamped to `AppSettings.MaxTurnsCeiling` (default 80) via `TaskRunner.ResolveMaxTurns` — a task or list override above the ceiling still starts, just capped, and a Warn is logged with the task id, requested, and effective value. Build CLI args via `ClaudeArgsBuilder`; inject attachment absolute paths via `TaskPromptComposer.Compose` (appends a read-only "## Reference files" section); invoke `ClaudeProcess` with task prompt
5. Stream NDJSON output through `StreamAnalyzer`; lines forwarded to log file and SignalR (`TaskMessage`)
6. On success: auto-commit changes (worktree only), store run record, mark "done"
7. On failure: retry once if session ID available (`--resume`), then mark "failed"
+5 -2
View File
@@ -45,7 +45,8 @@ public record AppSettingsDto(
List<string>? SessionSkills = null,
List<ModelPresetDto>? ModelPresets = null,
int UsageGateFiveHourPct = 80,
int UsageGateSevenDayPct = 90);
int UsageGateSevenDayPct = 90,
int MaxTurnsCeiling = 80);
// Per-model run defaults (effort + turn budget) edited in Settings -> General.
public record ModelPresetDto(string Model, string Effort, int MaxTurns);
@@ -382,7 +383,8 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
Data.Models.ModelPresets.Parse(row.ModelPresets)
.Select(p => new ModelPresetDto(p.Model, p.Effort, p.MaxTurns)).ToList(),
row.UsageGateFiveHourPct,
row.UsageGateSevenDayPct);
row.UsageGateSevenDayPct,
row.MaxTurnsCeiling);
}
public async Task UpdateAppSettings(AppSettingsDto dto)
@@ -412,6 +414,7 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
: Data.Models.ModelPresets.SerializeDefaults(),
UsageGateFiveHourPct = dto.UsageGateFiveHourPct,
UsageGateSevenDayPct = dto.UsageGateSevenDayPct,
MaxTurnsCeiling = dto.MaxTurnsCeiling,
});
}
+12 -3
View File
@@ -564,12 +564,21 @@ public sealed class TaskRunner
var model = task.Model ?? listConfig?.Model ?? global.DefaultModel;
var preset = Data.Models.ModelPresets.For(global.ModelPresets, model, global.DefaultMaxTurns);
var requestedMaxTurns = task.MaxTurns ?? listConfig?.MaxTurns ?? preset.MaxTurns;
var maxTurns = ResolveMaxTurns(task.MaxTurns, listConfig?.MaxTurns, preset.MaxTurns, global.MaxTurnsCeiling);
if (maxTurns < requestedMaxTurns)
{
_logger.LogWarning(
"Task {TaskId}: max turns clamped to ceiling (requested={Requested}, effective={Effective}, ceiling={Ceiling})",
task.Id, requestedMaxTurns, maxTurns, global.MaxTurnsCeiling);
}
return new ClaudeRunConfig(
Model: model,
SystemPrompt: string.IsNullOrWhiteSpace(instructions) ? null : instructions,
AgentPath: task.AgentPath ?? listConfig?.AgentPath,
ResumeSessionId: resumeSessionId,
MaxTurns: ResolveMaxTurns(task.MaxTurns, listConfig?.MaxTurns, preset.MaxTurns),
MaxTurns: maxTurns,
PermissionMode: global.DefaultPermissionMode,
SkillNames: skillNames,
Effort: preset.Effort);
@@ -628,8 +637,8 @@ public sealed class TaskRunner
return names;
}
internal static int? ResolveMaxTurns(int? taskTurns, int? listTurns, int globalDefault)
=> taskTurns ?? listTurns ?? globalDefault;
internal static int? ResolveMaxTurns(int? taskTurns, int? listTurns, int globalDefault, int ceiling)
=> Math.Min(taskTurns ?? listTurns ?? globalDefault, ceiling);
public static string MergeInstructions(params string?[] parts)
{