feat(worker): add post-merge verification gate for list merges
Per-list optional VerifyCommand (list_config.verify_command) runs via VerifyCommandRunner in the list's working dir right after a successful merge/continue-merge, before the task is allowed to reach Done. A non-zero exit or timeout leaves the merge in place but keeps the task out of Done and reports StatusVerifyFailed with an output excerpt through MergeResultDto/review_task; no command configured behaves exactly as before. Merges against the same repo are now serialized per working dir so a running verify can't be interrupted by a second merge landing mid-build. Adds the field to the List Settings modal (en/de localized) and covers success/failure/timeout in TaskMergeServiceTests + VerifyCommandRunnerTests.
This commit is contained in:
@@ -184,7 +184,10 @@ public sealed partial class AgentConfigEditorViewModel : ViewModelBase, IDisposa
|
||||
catch { }
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task SaveAsync()
|
||||
// verifyCommand is a List-only field owned by ListSettingsModalViewModel (not this editor,
|
||||
// which is also reused for Task scope); the caller passes it through so the single
|
||||
// UpdateListConfig call carries the full desired row instead of clobbering it.
|
||||
public async System.Threading.Tasks.Task SaveAsync(string? verifyCommand = null)
|
||||
{
|
||||
if (TargetId is null) return;
|
||||
var model = string.IsNullOrWhiteSpace(Model) ? null : Model;
|
||||
@@ -196,7 +199,7 @@ public sealed partial class AgentConfigEditorViewModel : ViewModelBase, IDisposa
|
||||
if (_scope == AgentConfigScope.Task)
|
||||
await _worker.UpdateTaskAgentSettingsAsync(new UpdateTaskAgentSettingsDto(TargetId, model, sp, ap, turns, skills));
|
||||
else
|
||||
await _worker.UpdateListConfigAsync(new UpdateListConfigDto(TargetId, model, sp, ap, turns, skills));
|
||||
await _worker.UpdateListConfigAsync(new UpdateListConfigDto(TargetId, model, sp, ap, turns, skills, verifyCommand));
|
||||
}
|
||||
|
||||
private List<string>? SelectedSessionSkillNames()
|
||||
|
||||
@@ -1073,6 +1073,10 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
var result = await _worker.ApproveReviewAsync(Task.Id, Merge.SelectedMergeTarget ?? "");
|
||||
if (!hasChildren && result?.Status == "conflict")
|
||||
await _merge.ResolveConflictAsync(Task.Id, Merge.SelectedMergeTarget ?? "");
|
||||
// The merge itself already landed; the verify command failed, so the task stayed
|
||||
// out of Done. Surface that instead of silently looking like nothing happened.
|
||||
else if (!hasChildren && result?.Status == "verify_failed" && ShowErrorAsync != null)
|
||||
await ShowErrorAsync(result.ErrorMessage ?? Loc.T("vm.detailsIsland.verifyFailed"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,9 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
||||
[ObservableProperty] private string _defaultCommitType = CommitTypeRegistry.DefaultType;
|
||||
// A manual list holds reminders: tasks created here start out manual (TaskEntity.IsManual).
|
||||
[ObservableProperty] private bool _isManual;
|
||||
// Optional post-merge verification command (build/test), run in WorkingDir after a merge
|
||||
// lands; a non-zero exit keeps the task out of Done instead of silently reporting merged.
|
||||
[ObservableProperty] private string _verifyCommand = "";
|
||||
|
||||
public ObservableCollection<string> CommitTypeOptions { get; } = new(CommitTypeRegistry.Types);
|
||||
|
||||
@@ -61,6 +64,8 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
||||
DefaultCommitType = string.IsNullOrWhiteSpace(defaultCommitType) ? CommitTypeRegistry.DefaultType : defaultCommitType;
|
||||
|
||||
await Agent.LoadForListAsync(listId, ct);
|
||||
var cfg = await _worker.GetListConfigAsync(listId);
|
||||
VerifyCommand = cfg?.VerifyCommand ?? "";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -73,7 +78,7 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
||||
DefaultCommitType,
|
||||
IsManual));
|
||||
|
||||
await Agent.SaveAsync();
|
||||
await Agent.SaveAsync(string.IsNullOrWhiteSpace(VerifyCommand) ? null : VerifyCommand);
|
||||
|
||||
CloseAction?.Invoke();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user