Ein Task kann jetzt auf beliebig viele Vorgaenger warten. Die Einzelspalte tasks.depends_on_task_id wird durch die Beziehungstabelle task_dependencies ersetzt (Composite-PK, beide FKs ON DELETE CASCADE -- das reproduziert das alte ON DELETE SET NULL: geloeschter Vorgaenger = Kante weg = Task wieder pickbar). BlockedByTaskId bleibt unberuehrt. - Migration AddTaskDependencies kopiert jede bestehende depends_on_task_id verlustfrei in die Tabelle, bevor die Spalte fallengelassen wird - QueuePicker gatet per doppelt negiertem NOT EXISTS: kein Task mit 0 Kanten wird blockiert, alle Kanten muessen done sein, eine Kante auf einen verschwundenen Task blockiert weiter - SetDependsOnAsync ersetzt die ganze Menge; Zykluspruefung ist jetzt eine BFS ueber den Graphen, faengt also auch A->B->C->A - blockedReason nennt alle offenen Vorgaenger (External/TaskBlocking, von ExternalMcpService und TaskWaitMcpTools gemeinsam genutzt) - add_task/update_task nehmen dependsOnTaskIds; die alte Skalarform bleibt als Ein-Element-Menge inkl. ""-Clear-Sentinel gueltig - UI: Fan-in-Zeile bleibt flach und listet alle Vorgaenger im After-Label, die lineare Kette rendert unveraendert
52 lines
3.4 KiB
C#
52 lines
3.4 KiB
C#
namespace ClaudeDo.Worker.State;
|
|
|
|
public interface ITaskStateService
|
|
{
|
|
Task<TransitionResult> EnqueueAsync(string taskId, CancellationToken ct);
|
|
Task<TransitionResult> StartRunningAsync(string taskId, DateTime startedAt, CancellationToken ct);
|
|
Task<TransitionResult> CompleteAsync(string taskId, DateTime finishedAt, string? result, CancellationToken ct);
|
|
Task<TransitionResult> SubmitForReviewAsync(string taskId, DateTime finishedAt, string? result, CancellationToken ct);
|
|
Task<TransitionResult> SubmitInteractiveForReviewAsync(string taskId, DateTime finishedAt, CancellationToken ct);
|
|
Task<TransitionResult> SubmitForChildrenAsync(string taskId, DateTime finishedAt, string? result, CancellationToken ct);
|
|
Task<TransitionResult> FailAsync(
|
|
string taskId, DateTime finishedAt, string? error, CancellationToken ct,
|
|
string failureReason = "error", int? turnsUsed = null, int? maxTurns = null);
|
|
Task<TransitionResult> CancelAsync(string taskId, DateTime finishedAt, CancellationToken ct, bool allowFromIdle = false);
|
|
Task<TransitionResult> ResetToIdleAsync(string taskId, CancellationToken ct);
|
|
|
|
Task<TransitionResult> ApproveReviewAsync(string taskId, CancellationToken ct);
|
|
Task<TransitionResult> RejectToQueueAsync(string taskId, string feedback, CancellationToken ct);
|
|
Task<TransitionResult> RejectToIdleAsync(string taskId, CancellationToken ct);
|
|
Task<TransitionResult> ClearReviewFeedbackAsync(string taskId, CancellationToken ct);
|
|
|
|
Task<TransitionResult> ForceSetStatusAsync(string taskId, ClaudeDo.Data.Models.TaskStatus status, CancellationToken ct);
|
|
|
|
// Guarded "manual done toggle" affordance (task list checkbox): unlike ForceSetStatusAsync,
|
|
// these check the expected starting status in the same ExecuteUpdateAsync filter so a
|
|
// concurrent picker claim (Idle/Done -> Running) is not silently overwritten.
|
|
Task<TransitionResult> MarkDoneAsync(string taskId, DateTime finishedAt, CancellationToken ct);
|
|
Task<TransitionResult> UnmarkDoneAsync(string taskId, CancellationToken ct);
|
|
|
|
// Guarded "remove from queue" affordance. Expects the task to still be Queued; clears
|
|
// BlockedByTaskId in the same update so a queued chain child never ends up Idle-but-blocked.
|
|
Task<TransitionResult> DequeueToIdleAsync(string taskId, CancellationToken ct);
|
|
|
|
Task<TransitionResult> StartPlanningAsync(string parentId, CancellationToken ct);
|
|
Task<TransitionResult> FinalizePlanningAsync(string parentId, CancellationToken ct);
|
|
|
|
Task<TransitionResult> BlockOnAsync(string taskId, string predecessorTaskId, CancellationToken ct);
|
|
Task<TransitionResult> UnblockAsync(string taskId, CancellationToken ct);
|
|
|
|
// Replaces the task's whole dependency set; an empty list clears it. Rejects a self-reference,
|
|
// an unknown dependency id, and any link that would create a cycle in the dependency graph
|
|
// -- see TaskStateService for the walk.
|
|
Task<TransitionResult> SetDependsOnAsync(string taskId, IReadOnlyList<string> dependsOnTaskIds, CancellationToken ct);
|
|
|
|
// Surfaces a WaitingForChildren parent for review once all its children are terminal.
|
|
// Best-effort (swallows and logs failures) — safe to call after any child mutation,
|
|
// e.g. deleting the last non-terminal child (no terminal transition fires for a delete).
|
|
Task TryAdvanceParentAsync(string parentId);
|
|
|
|
Task<int> RecoverStaleRunningAsync(string reason, CancellationToken ct);
|
|
}
|