Three coordinated guards close the orphan-creation paths: - CreateChildAsync refuses when the parent is not in a planning phase. - DiscardPlanningAsync now returns a structured DiscardPlanningOutcome and refuses when children are queued or running; callers can opt into auto-dequeuing queued kids via dequeueQueuedChildren=true. Terminal children (Done/Failed/Cancelled) are promoted to top-level instead of becoming orphans when the parent's PlanningPhase is reset. - OrphanRecovery hosted service clears ParentTaskId on any rows whose parent is missing or no longer in a planning phase on worker startup, mirroring the StaleTaskRecovery pattern. UI surfaces the block reason: a confirm dialog offers to dequeue queued children and retry; a running-children block is shown as a hard error asking the user to cancel first. WorkerClient now negotiates the JsonStringEnumConverter so the DiscardPlanningResult enum round-trips correctly over SignalR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19 lines
685 B
C#
19 lines
685 B
C#
namespace ClaudeDo.Data.Repositories;
|
|
|
|
public enum DiscardPlanningResult
|
|
{
|
|
/// <summary>Planning state cleared, children handled.</summary>
|
|
Discarded,
|
|
/// <summary>Parent not found or not in <c>PlanningPhase.Active</c>.</summary>
|
|
NotInPlanning,
|
|
/// <summary>At least one child is <c>Queued</c> and the caller did not opt in to auto-dequeue.</summary>
|
|
BlockedByQueuedChildren,
|
|
/// <summary>At least one child is <c>Running</c>; user must cancel it before discarding.</summary>
|
|
BlockedByRunningChildren,
|
|
}
|
|
|
|
public readonly record struct DiscardPlanningOutcome(
|
|
DiscardPlanningResult Result,
|
|
int QueuedChildrenCount,
|
|
int RunningChildrenCount);
|