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:
mika kuns
2026-08-05 11:19:22 +02:00
parent 334cf1e1d2
commit c9ba1e2645
30 changed files with 1469 additions and 126 deletions
@@ -96,6 +96,38 @@ public sealed class ListRepositoryConfigTests : IDisposable
Assert.Null(fetched.SessionSkills);
}
[Fact]
public async Task SetConfig_Persists_VerifyCommand_On_Insert()
{
await _repo.SetConfigAsync(new ListConfigEntity { ListId = _listId, VerifyCommand = "dotnet test" });
var fetched = await _repo.GetConfigAsync(_listId);
Assert.NotNull(fetched);
Assert.Equal("dotnet test", fetched.VerifyCommand);
}
[Fact]
public async Task SetConfig_Persists_VerifyCommand_On_Update()
{
await _repo.SetConfigAsync(new ListConfigEntity { ListId = _listId, VerifyCommand = "dotnet build" });
await _repo.SetConfigAsync(new ListConfigEntity { ListId = _listId, VerifyCommand = "dotnet test" });
var fetched = await _repo.GetConfigAsync(_listId);
Assert.NotNull(fetched);
Assert.Equal("dotnet test", fetched.VerifyCommand);
}
[Fact]
public async Task SetConfig_Null_VerifyCommand_Clears_On_Update()
{
await _repo.SetConfigAsync(new ListConfigEntity { ListId = _listId, VerifyCommand = "dotnet test" });
await _repo.SetConfigAsync(new ListConfigEntity { ListId = _listId, VerifyCommand = null });
var fetched = await _repo.GetConfigAsync(_listId);
Assert.NotNull(fetched);
Assert.Null(fetched.VerifyCommand);
}
public void Dispose()
{
_ctx.Dispose();