feat: planning sessions foundation (Plan A) #4

Merged
claude merged 16 commits from feat/planning-sessions-foundation into main 2026-04-23 16:31:37 +00:00
2 changed files with 96 additions and 0 deletions
Showing only changes of commit b466246c1b - Show all commits

View File

@@ -206,6 +206,19 @@ public sealed class TaskRepository
#endregion
#region Planning
public async Task<List<TaskEntity>> GetChildrenAsync(string parentId, CancellationToken ct = default)
{
return await _context.Tasks
.AsNoTracking()
.Where(t => t.ParentTaskId == parentId)
.OrderBy(t => t.SortOrder).ThenBy(t => t.CreatedAt)
.ToListAsync(ct);
}
#endregion
#region Queue selection
public async Task<TaskEntity?> GetNextQueuedAgentTaskAsync(DateTime now, CancellationToken ct = default)

View File

@@ -0,0 +1,83 @@
using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Tests.Infrastructure;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
namespace ClaudeDo.Worker.Tests.Repositories;
public sealed class TaskRepositoryPlanningTests : IDisposable
{
private readonly DbFixture _db = new();
private readonly ClaudeDoDbContext _ctx;
private readonly TaskRepository _tasks;
private readonly ListRepository _lists;
private readonly TagRepository _tags;
public TaskRepositoryPlanningTests()
{
_ctx = _db.CreateContext();
_tasks = new TaskRepository(_ctx);
_lists = new ListRepository(_ctx);
_tags = new TagRepository(_ctx);
}
public void Dispose()
{
_ctx.Dispose();
_db.Dispose();
}
private async Task<string> CreateListAsync(string? id = null)
{
var listId = id ?? Guid.NewGuid().ToString();
await _lists.AddAsync(new ListEntity
{
Id = listId,
Name = "Test List",
CreatedAt = DateTime.UtcNow,
});
return listId;
}
private TaskEntity MakeTask(string listId, TaskStatus status = TaskStatus.Manual, string? parentId = null) => new()
{
Id = Guid.NewGuid().ToString(),
ListId = listId,
Title = "t",
Status = status,
CreatedAt = DateTime.UtcNow,
CommitType = "feat",
ParentTaskId = parentId,
};
[Fact]
public async Task GetChildrenAsync_ReturnsOnlyDirectChildren_Sorted()
{
var listId = await CreateListAsync();
var parent = MakeTask(listId, TaskStatus.Planning);
parent.Title = "parent";
await _tasks.AddAsync(parent);
var childA = MakeTask(listId, TaskStatus.Draft, parentId: parent.Id);
childA.Title = "a";
await _tasks.AddAsync(childA);
childA.SortOrder = 1;
await _tasks.UpdateAsync(childA);
var childB = MakeTask(listId, TaskStatus.Draft, parentId: parent.Id);
childB.Title = "b";
await _tasks.AddAsync(childB);
childB.SortOrder = 0;
await _tasks.UpdateAsync(childB);
var unrelated = MakeTask(listId, TaskStatus.Manual);
await _tasks.AddAsync(unrelated);
var children = await _tasks.GetChildrenAsync(parent.Id);
Assert.Equal(2, children.Count);
Assert.Equal("b", children[0].Title);
Assert.Equal("a", children[1].Title);
}
}