refactor: Datei-Scope-Serialisierung entfernen

ScopeGlobs auf Tasks und SerializeOnFileOverlap auf der Listen-Config waren
ungenutzt: der Scope wurde nie befuellt, also hat der Queue-Picker nie
serialisiert. ScopeOverlap, das Picker-Gate, die DTO-Felder, die UI-Option und
die Spalten fallen weg (Migration DropFileScopeSerialization).
This commit is contained in:
mika kuns
2026-08-27 16:43:14 +02:00
parent 4e55f9161c
commit 84219a9f88
25 changed files with 1051 additions and 482 deletions
@@ -50,7 +50,6 @@ public sealed class QueuePickerTests : IDisposable
bool taskAgentTag = false,
int? sortOrder = null,
bool isManual = false,
string? scopeGlobs = null,
string? dependsOn = null)
{
var task = new TaskEntity
@@ -65,7 +64,6 @@ public sealed class QueuePickerTests : IDisposable
DependsOnTaskId = dependsOn,
CommitType = "feat",
IsManual = isManual,
ScopeGlobs = scopeGlobs,
};
await _tasks.AddAsync(task);
if (sortOrder is not null)
@@ -76,25 +74,6 @@ public sealed class QueuePickerTests : IDisposable
return task;
}
private async Task SetSerializeOnFileOverlapAsync(string listId, bool value)
=> await _lists.SetConfigAsync(new ListConfigEntity { ListId = listId, SerializeOnFileOverlap = value });
private async Task SeedWorktreeAsync(string taskId, WorktreeState state, string? diffStat)
{
_ctx.Worktrees.Add(new WorktreeEntity
{
TaskId = taskId,
Path = $"C:\\fake\\{taskId}",
BranchName = $"claudedo/{taskId[..8]}",
BaseCommit = "base",
HeadCommit = "head",
DiffStat = diffStat,
State = state,
CreatedAt = DateTime.UtcNow,
});
await _ctx.SaveChangesAsync();
}
[Fact]
public async Task ClaimNextAsync_Skips_ManualTasks()
{
@@ -260,86 +239,4 @@ public sealed class QueuePickerTests : IDisposable
var nonNull = results.Where(r => r is not null).ToList();
Assert.Single(nonNull);
}
[Fact]
public async Task ClaimNextAsync_SerializeOff_ClaimsOverlappingQueuedTask_EvenWithRunningOverlap()
{
// Default (option off): behavior is unaffected by ScopeGlobs, even when it overlaps a
// running sibling's declared scope.
var listId = await CreateListAsync();
await SeedAsync(listId, status: TaskStatus.Running, scopeGlobs: "src/Foo.cs");
var queued = await SeedAsync(listId, scopeGlobs: "src/Foo.cs");
var picked = await _picker.ClaimNextAsync(DateTime.UtcNow, CancellationToken.None);
Assert.NotNull(picked);
Assert.Equal(queued.Id, picked!.Id);
}
[Fact]
public async Task ClaimNextAsync_SerializeOn_SkipsQueuedTask_OverlappingRunningSibling()
{
var listId = await CreateListAsync();
await SetSerializeOnFileOverlapAsync(listId, true);
await SeedAsync(listId, status: TaskStatus.Running, scopeGlobs: "src/Foo.cs");
var overlapping = await SeedAsync(listId, scopeGlobs: "src/Foo.cs", sortOrder: 0, createdAt: DateTime.UtcNow.AddMinutes(-5));
var clear = await SeedAsync(listId, scopeGlobs: "src/Bar.cs", sortOrder: 1, createdAt: DateTime.UtcNow);
var picked = await _picker.ClaimNextAsync(DateTime.UtcNow, CancellationToken.None);
Assert.NotNull(picked);
Assert.Equal(clear.Id, picked!.Id);
var stillQueued = await _tasks.GetByIdAsync(overlapping.Id);
Assert.Equal(TaskStatus.Queued, stillQueued!.Status);
}
[Fact]
public async Task ClaimNextAsync_SerializeOn_NoDeclaredScope_StillClaimed()
{
var listId = await CreateListAsync();
await SetSerializeOnFileOverlapAsync(listId, true);
await SeedAsync(listId, status: TaskStatus.Running, scopeGlobs: "src/Foo.cs");
var queued = await SeedAsync(listId); // no ScopeGlobs declared -- no basis to hold it back
var picked = await _picker.ClaimNextAsync(DateTime.UtcNow, CancellationToken.None);
Assert.NotNull(picked);
Assert.Equal(queued.Id, picked!.Id);
}
[Fact]
public async Task ClaimNextAsync_SerializeOn_SkipsQueuedTask_OverlappingUnmergedFinishedSiblingDiff()
{
var listId = await CreateListAsync();
await SetSerializeOnFileOverlapAsync(listId, true);
var finished = await SeedAsync(listId, status: TaskStatus.WaitingForReview);
await SeedWorktreeAsync(finished.Id, WorktreeState.Active, "src/Foo.cs | 3 ++-");
await SeedAsync(listId, scopeGlobs: "src/Foo.cs");
var picked = await _picker.ClaimNextAsync(DateTime.UtcNow, CancellationToken.None);
Assert.Null(picked);
}
[Fact]
public async Task ClaimNextAsync_SerializeOn_IgnoresMergedSiblingDiff()
{
// A merged sibling's changes are already on the base branch -- not a live conflict risk,
// so it must not hold back an overlapping queued task.
var listId = await CreateListAsync();
await SetSerializeOnFileOverlapAsync(listId, true);
var merged = await SeedAsync(listId, status: TaskStatus.Done);
await SeedWorktreeAsync(merged.Id, WorktreeState.Merged, "src/Foo.cs | 3 ++-");
var queued = await SeedAsync(listId, scopeGlobs: "src/Foo.cs");
var picked = await _picker.ClaimNextAsync(DateTime.UtcNow, CancellationToken.None);
Assert.NotNull(picked);
Assert.Equal(queued.Id, picked!.Id);
}
}