feat(data): per-list base branch, preserved by every config writer

This commit is contained in:
mika kuns
2026-10-01 10:39:49 +02:00
parent 8911408bca
commit 3fe1feadf9
10 changed files with 1321 additions and 9 deletions
+8 -5
View File
@@ -10,7 +10,7 @@ using ModelContextProtocol.Server;
namespace ClaudeDo.Worker.External;
public sealed record TaskConfigDto(string? Model, string? SystemPrompt, string? AgentPath, int? MaxTurns);
public sealed record ListConfigDto(string? Model, string? SystemPrompt, string? AgentPath, int? MaxTurns, string? VerifyCommand);
public sealed record ListConfigDto(string? Model, string? SystemPrompt, string? AgentPath, int? MaxTurns, string? VerifyCommand, string? BaseBranch = null);
public sealed record TaskConfigResult(bool Found, TaskConfigDto? Config);
public sealed record ListConfigResult(bool Found, ListConfigDto? Config);
public sealed record SetListConfigResult(bool Ok, string ListId, ListConfigDto? Config);
@@ -55,7 +55,7 @@ public sealed class ConfigMcpTools
var cfg = await _lists.GetConfigAsync(listId, cancellationToken);
return cfg is null
? new ListConfigResult(false, null)
: new ListConfigResult(true, new ListConfigDto(cfg.Model, cfg.SystemPrompt, cfg.AgentPath, cfg.MaxTurns, cfg.VerifyCommand));
: new ListConfigResult(true, new ListConfigDto(cfg.Model, cfg.SystemPrompt, cfg.AgentPath, cfg.MaxTurns, cfg.VerifyCommand, cfg.BaseBranch));
}
// clearFields (not a per-field sentinel like UpdateList's "" for workingDir) because maxTurns is an
@@ -81,7 +81,7 @@ public sealed class ConfigMcpTools
"in this list that don't override them (verify command is list-only; tasks cannot override it). Only " +
"the fields you pass are changed; omitted fields keep their current value. To clear a field instead, " +
"name it in clearFields; clearing all five deletes the list's config unless it also carries settings " +
"this tool doesn't expose (session skills, file-scope serialization, permission mode), which are " +
"this tool doesn't expose (session skills, permission mode, ticket link, base branch, PowerUse opt-in), which are " +
"always preserved.")]
public async Task<SetListConfigResult> SetListConfig(
string listId, string? model = null, string? systemPrompt = null, string? agentPath = null,
@@ -110,7 +110,8 @@ public sealed class ConfigMcpTools
// UI-only).
var hasUnrelatedSettings = existing is not null
&& (existing.SessionSkills is not null
|| existing.PermissionMode is not null || existing.TicketProjectId is not null);
|| existing.PermissionMode is not null || existing.TicketProjectId is not null
|| existing.BaseBranch is not null || existing.PowerUseAllowed);
ListConfigDto? config;
var allCleared = m is null && sp is null && ap is null && mt is null && vc is null;
@@ -131,8 +132,10 @@ public sealed class ConfigMcpTools
VerifyCommand = vc, SessionSkills = existing?.SessionSkills,
PermissionMode = existing?.PermissionMode,
TicketProjectId = existing?.TicketProjectId,
BaseBranch = existing?.BaseBranch,
PowerUseAllowed = existing?.PowerUseAllowed ?? false,
}, cancellationToken);
config = allCleared ? null : new ListConfigDto(m, sp, ap, mt, vc);
config = allCleared ? null : new ListConfigDto(m, sp, ap, mt, vc, existing?.BaseBranch);
}
await _broadcaster.ListUpdated(listId);
+6 -2
View File
@@ -600,9 +600,12 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
// Gleiches Tri-State-Muster: null = gespeicherten Wert behalten, false = Opt-in widerrufen.
var powerUseAllowed = dto.PowerUseAllowed ?? existing?.PowerUseAllowed ?? false;
// Tri-State wie oben: null = behalten, "" = löschen.
var baseBranch = dto.BaseBranch is null ? existing?.BaseBranch : dto.BaseBranch.NullIfBlank();
// powerUseAllowed muss mit in die Leer-Prüfung: sonst löscht ein Save, bei dem PowerUse das
// EINZIGE gesetzte Feld ist, die Config-Zeile und damit das Opt-in gleich wieder mit.
if (model is null && systemPrompt is null && agentPath is null && dto.MaxTurns is null && sessionSkills is null && verifyCommand is null && permissionMode is null && ticketProjectId is null && !powerUseAllowed)
if (model is null && systemPrompt is null && agentPath is null && dto.MaxTurns is null && sessionSkills is null && verifyCommand is null && permissionMode is null && ticketProjectId is null && !powerUseAllowed && baseBranch is null)
{
await repo.DeleteConfigAsync(dto.ListId);
}
@@ -620,6 +623,7 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
PermissionMode = permissionMode,
TicketProjectId = ticketProjectId,
PowerUseAllowed = powerUseAllowed,
BaseBranch = baseBranch,
});
}
@@ -643,7 +647,7 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
var repo = new ListRepository(ctx);
var config = await repo.GetConfigAsync(listId);
if (config is null) return null;
return new ListConfigDto(config.Model, config.SystemPrompt, config.AgentPath, config.MaxTurns, SkillsFromJson(config.SessionSkills), config.VerifyCommand, config.PermissionMode, config.TicketProjectId, config.PowerUseAllowed);
return new ListConfigDto(config.Model, config.SystemPrompt, config.AgentPath, config.MaxTurns, SkillsFromJson(config.SessionSkills), config.VerifyCommand, config.PermissionMode, config.TicketProjectId, config.PowerUseAllowed, config.BaseBranch);
}
public async Task<SetTaskStatusResultDto> SetTaskStatus(string taskId, string status)