using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
namespace ClaudeDo.Worker.External;
///
/// Renders the `blockedReason` string shared by and
/// . Lives here so the two can't drift on the wording an agent
/// parses.
///
internal static class TaskBlocking
{
///
/// The predecessors of that have not reached Done — an id
/// missing from counts as unmet (the picker treats a dangling
/// edge as blocking too).
///
public static List<(string Id, TaskStatus? Status)> Unmet(
IEnumerable dependencyIds, IReadOnlyDictionary statuses)
{
var unmet = new List<(string Id, TaskStatus? Status)>();
foreach (var id in dependencyIds)
{
if (statuses.TryGetValue(id, out var status))
{
if (status != TaskStatus.Done) unmet.Add((id, status));
}
else
{
unmet.Add((id, null));
}
}
return unmet;
}
/// Names every open predecessor, not just the first — a task can wait on n of them.
public static string Describe(IReadOnlyList<(string Id, TaskStatus? Status)> unmet)
{
var noun = unmet.Count == 1 ? "task" : "tasks";
var parts = unmet.Select(u => $"{u.Id} (status: {(u.Status?.ToString() ?? "not found")})");
return $"Blocked: depends on {noun} {string.Join(", ", parts)}.";
}
}