feat(data): add subtasks table, repository and prompt integration

Per-task checklist backend: subtasks table with CASCADE delete,
SubtaskEntity + SubtaskRepository (connection-per-op, async), DI
registration in App and Worker, TaskRunner composes a '## Sub-Tasks'
markdown block into the Claude prompt when subtasks exist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mika Kuns
2026-04-15 11:19:54 +02:00
parent 8577c55685
commit 8c051d8f62
7 changed files with 121 additions and 5 deletions

View File

@@ -19,6 +19,7 @@ builder.Services.AddSingleton(dbFactory);
builder.Services.AddSingleton<TagRepository>();
builder.Services.AddSingleton<ListRepository>();
builder.Services.AddSingleton<TaskRepository>();
builder.Services.AddSingleton<SubtaskRepository>();
builder.Services.AddSingleton<WorktreeRepository>();
builder.Services.AddSingleton<TaskRunRepository>();
builder.Services.AddHostedService<StaleTaskRecovery>();

View File

@@ -12,6 +12,7 @@ public sealed class TaskRunner
private readonly TaskRunRepository _runRepo;
private readonly ListRepository _listRepo;
private readonly WorktreeRepository _wtRepo;
private readonly SubtaskRepository _subtaskRepo;
private readonly HubBroadcaster _broadcaster;
private readonly WorktreeManager _wtManager;
private readonly ClaudeArgsBuilder _argsBuilder;
@@ -24,6 +25,7 @@ public sealed class TaskRunner
TaskRunRepository runRepo,
ListRepository listRepo,
WorktreeRepository wtRepo,
SubtaskRepository subtaskRepo,
HubBroadcaster broadcaster,
WorktreeManager wtManager,
ClaudeArgsBuilder argsBuilder,
@@ -35,6 +37,7 @@ public sealed class TaskRunner
_runRepo = runRepo;
_listRepo = listRepo;
_wtRepo = wtRepo;
_subtaskRepo = subtaskRepo;
_broadcaster = broadcaster;
_wtManager = wtManager;
_argsBuilder = argsBuilder;
@@ -91,9 +94,16 @@ public sealed class TaskRunner
await _broadcaster.TaskStarted(slot, task.Id, now);
// Build prompt.
var prompt = string.IsNullOrWhiteSpace(task.Description)
? task.Title
: $"{task.Title}\n\n{task.Description.Trim()}";
var subtasks = await _subtaskRepo.GetByTaskIdAsync(task.Id, ct);
var sb = new System.Text.StringBuilder(task.Title);
if (!string.IsNullOrWhiteSpace(task.Description)) sb.Append("\n\n").Append(task.Description.Trim());
if (subtasks.Count > 0)
{
sb.Append("\n\n## Sub-Tasks\n");
foreach (var s in subtasks)
sb.Append(s.Completed ? "- [x] " : "- [ ] ").Append(s.Title).Append('\n');
}
var prompt = sb.ToString();
// Run 1.
var result = await RunOnceAsync(task.Id, slot, runDir, resolvedConfig, 1, false, prompt, ct);