# Inherited settings display, per-task overrides, and Turns **Date:** 2026-06-04 **Status:** Approved (design) ## Problem Config inheritance is three-tier (Task → List → Global app settings). Today the UI only signals inheritance with a placeholder sentinel (`(inherit)` for tasks, `(default)` for lists) and, for tasks, a faint "Effective if inherited: {value}" hint under Model and Agent. Two gaps: 1. You can't see the *actual resolved value* an inherited field will use, nor where it comes from (List vs Global). 2. **Max turns** is global-only (`AppSettingsEntity.DefaultMaxTurns` = 100). It is not overridable per list or per task, unlike Model / SystemPrompt / AgentPath. ## Goals - Show the real inherited value in-place, muted, with a **source-aware marker** (`inherited · List` vs `inherited · Global`). Picking a value turns it into an override; a reset affordance clears it back to inherited. - Add **Turns** (max turns) as an overridable field at both List and Task levels, inheriting from the global default. Numeric box; empty = inherit. - Keep SystemPrompt as-is (it is additive, not override) but show what gets prepended. ## Non-goals - No change to SystemPrompt merge semantics (stays additive/concatenated). - No new global settings; `DefaultMaxTurns` already exists. - No change to PermissionMode handling. ## Inheritance semantics (reference) Resolved in `TaskRunner.BuildRunConfig` (~line 388): | Field | Semantics | Resolution | |--------------|------------|--------------------------------------------------------| | Model | override | `task.Model ?? listConfig?.Model ?? global.DefaultModel` | | AgentPath | override | `task.AgentPath ?? listConfig?.AgentPath` (no global) | | MaxTurns | override | **new:** `task.MaxTurns ?? listConfig?.MaxTurns ?? global.DefaultMaxTurns` | | SystemPrompt | additive | merged: global + list + task + agent (unchanged) | Lists inherit only from Global (no tier above them), so a list's inherited marker is always `inherited · Global`. ## Design ### 1. Data layer - `ListConfigEntity`: add `int? MaxTurns`. - `TaskEntity`: add `int? MaxTurns` (nullable override). - EF Core migration adding `max_turns` column to `list_config` and `tasks` (nullable, no default — null = inherit). - `TaskRunner` BuildRunConfig: `MaxTurns: task.MaxTurns ?? listConfig?.MaxTurns ?? global.DefaultMaxTurns`. `ClaudeRunConfig.MaxTurns` and `ClaudeArgsBuilder` already accept/emit `--max-turns` when `> 0` — no change needed there. - `ListRepository.SetConfigAsync` (upsert) and `TaskRepository.UpdateAgentSettingsAsync` extend to carry `maxTurns`. ### 2. DTOs / transport Add `int? MaxTurns` to (Worker + Ui copies kept in sync): - `UpdateListConfigDto`, `ListConfigDto` (WorkerHub.cs + WorkerClient.cs) - `UpdateTaskAgentSettingsDto` (WorkerHub.cs + WorkerClient.cs) - `TaskConfigDto` (ConfigMcpTools.cs) `WorkerHub.UpdateListConfig` / `UpdateTaskAgentSettings` persist the new field via the repositories above. MCP `SetListConfig` / `SetTaskConfig` gain an optional `maxTurns` parameter to keep the agent-facing API at parity with the UI. ### 3. Resolution helper (Ui) A small helper that, given `(taskValue, listValue, globalValue)`, returns `(effectiveValue, source)` where `source ∈ { Override, List, Global }`. Drives the marker text and muted/normal styling for Model, Agent, and Turns so the logic isn't duplicated per field or per editor. Lives in the Ui layer beside its consumers. ### 4. UI rendering — inherited marker (source-aware) For **Model**, **Agent**, **Turns** in both `ListSettingsModalView` and the DetailsIsland "Agent settings (overrides)" expander: - Remove the `(inherit)` / `(default)` sentinel *row* from the control's item source. - When no override is set: control shows the **resolved value muted/greyed** (dropdown shows e.g. "sonnet" dimmed; Turns box shows e.g. "100" as a muted placeholder), and a small badge beside the field label reads `inherited · List` or `inherited · Global`. - On picking a value / typing a number: it becomes an override — text returns to normal color, the badge flips to `override` (or hides), and a small **"↺ reset to inherited"** affordance appears that clears the value back to null. - List modal: source is always Global → badge reads `inherited · Global`; reset clears to the global default. - Turns: numeric box, empty = inherit (muted resolved number as placeholder); a typed number is the override. **Rendering approach:** a small reusable `InheritedFieldHeader` control (label + badge + reset button), fed by the resolution helper's `source`, wraps each field. Keeps the three fields consistent and avoids per-field XAML duplication. Badge / muted styling uses existing design tokens. Visual polish pass is the user's. ### 5. SystemPrompt (stays plain) SystemPrompt keeps its plain multi-line text box (additive, not override). Below it, a small **read-only, collapsed-by-default** hint shows the inherited prompts that will be prepended (global + list), labeled e.g. "Prepended automatically:". No marker, no reset — it never replaces, only appends. ### 6. Localization New keys in `locales/en.json` + `locales/de.json` (parity enforced by Localization.Tests): marker text (`inherited · List`, `inherited · Global`, `override`), reset affordance label, Turns field label, and the SystemPrompt "prepended automatically" hint. Retire the now-unused `vm.details.effectiveIfInherited` key (and its German counterpart) if nothing else references it. ## Affected files (indicative) - `src/ClaudeDo.Data/Models/ListConfigEntity.cs`, `TaskEntity.cs` - `src/ClaudeDo.Data/Migrations/` (new migration) - `src/ClaudeDo.Data/Repositories/ListRepository.cs`, `TaskRepository.cs` - `src/ClaudeDo.Data/Configuration/` (column mapping for `max_turns`) - `src/ClaudeDo.Worker/Runner/TaskRunner.cs` - `src/ClaudeDo.Worker/Hub/WorkerHub.cs` - `src/ClaudeDo.Worker/External/ConfigMcpTools.cs` - `src/ClaudeDo.Ui/Services/WorkerClient.cs` (+ `Interfaces/IWorkerClient.cs`) - `src/ClaudeDo.Ui/ViewModels/Modals/ListSettingsModalViewModel.cs` + view - `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs` + `DetailsIslandView.axaml` - `src/ClaudeDo.Ui/Views/Controls/` (new `InheritedFieldHeader`) - `src/ClaudeDo.Ui/` resolution helper - `locales/en.json`, `locales/de.json` ## Testing - Data: migration applies; `MaxTurns` round-trips through `ListRepository.SetConfigAsync` and `TaskRepository.UpdateAgentSettingsAsync`. - Worker: `BuildRunConfig` resolves MaxTurns via task → list → global precedence (unit test on the resolution). Existing `ClaudeArgsBuilder` `--max-turns` behavior unchanged. - Ui: resolution helper returns correct `(value, source)` for each of the override / list / global cases across Model, Agent, Turns. - Localization: en/de key parity (existing Localization.Tests). - Test fakes: update hand-rolled `IWorkerClient` fakes in both test projects for the new DTO fields (per known gotcha). - Visual verification of the marker / muted styling: flagged for the user (cannot be asserted programmatically). ## Open risks - DTO/ctor changes ripple into hand-rolled test fakes in Worker.Tests and Ui.Tests — must be updated in the same change. - Removing the sentinel row from dropdowns changes selection binding; ensure null/empty override state is represented without a sentinel item (e.g. dropdown `SelectedItem` null when inherited).