Planning subtasks are now "Draft" until their parent plan is finalized, then "Planned" (queueable). Finalizing a plan no longer auto-queues the child chain; the user sends the plan to the queue explicitly. - TaskStateService rejects a child entering Queued/Running unless its parent is Finalized; this single invariant covers UI, queue, RunNow and MCP paths - WorkerHub.SetTaskStatus routes Queued through the gated EnqueueAsync - Finalize call sites pass queueAgentTasks: false - PlanningChainCoordinator.QueuePlanAsync guards the chain build on Finalized - TaskRowViewModel derives Draft/Planned from ParentFinalized; gates CanSendToQueue / CanQueuePlan; view shows a PLANNED badge Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
101 lines
2.8 KiB
C#
101 lines
2.8 KiB
C#
using ClaudeDo.Data.Models;
|
|
using ClaudeDo.Ui.ViewModels.Islands;
|
|
using Xunit;
|
|
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
|
|
|
namespace ClaudeDo.Worker.Tests.UiVm;
|
|
|
|
public class TaskRowViewModelPlanningTests
|
|
{
|
|
private static TaskRowViewModel MakeRow(
|
|
TaskStatus status,
|
|
string? parentTaskId = null,
|
|
PlanningPhase phase = PlanningPhase.None)
|
|
=> new TaskRowViewModel { Id = "t", Status = status, ParentTaskId = parentTaskId, PlanningPhase = phase };
|
|
|
|
[Fact]
|
|
public void IdleChild_IsDraft_WhenParentIdIsNotNull()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle, parentTaskId: "parent-id");
|
|
Assert.True(vm.IsChild);
|
|
Assert.True(vm.IsDraft);
|
|
Assert.False(vm.IsPlanningParent);
|
|
}
|
|
|
|
[Fact]
|
|
public void ActivePlanning_SetsIsPlanningParent()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle, phase: PlanningPhase.Active);
|
|
Assert.True(vm.IsPlanningParent);
|
|
Assert.False(vm.IsChild);
|
|
Assert.Equal("PLANNING", vm.PlanningBadge);
|
|
}
|
|
|
|
[Fact]
|
|
public void FinalizedPlanning_ShowsPlannedBadge()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle, phase: PlanningPhase.Finalized);
|
|
Assert.True(vm.IsPlanningParent);
|
|
Assert.Equal("PLANNED", vm.PlanningBadge);
|
|
}
|
|
|
|
[Fact]
|
|
public void PlainIdle_NoBadge()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle);
|
|
Assert.False(vm.IsPlanningParent);
|
|
Assert.Null(vm.PlanningBadge);
|
|
}
|
|
|
|
[Fact]
|
|
public void DraftChild_CannotSendToQueue()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle, parentTaskId: "parent-id");
|
|
vm.ParentFinalized = false;
|
|
Assert.True(vm.IsDraft);
|
|
Assert.False(vm.IsPlanned);
|
|
Assert.False(vm.CanSendToQueue);
|
|
}
|
|
|
|
[Fact]
|
|
public void PlannedChild_CanSendToQueue()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle, parentTaskId: "parent-id");
|
|
vm.ParentFinalized = true;
|
|
Assert.False(vm.IsDraft);
|
|
Assert.True(vm.IsPlanned);
|
|
Assert.True(vm.CanSendToQueue);
|
|
}
|
|
|
|
[Fact]
|
|
public void StandaloneIdle_CanSendToQueue()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle);
|
|
Assert.False(vm.IsChild);
|
|
Assert.True(vm.CanSendToQueue);
|
|
}
|
|
|
|
[Fact]
|
|
public void FinalizedParentWithChildren_CanQueuePlan()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle, phase: PlanningPhase.Finalized);
|
|
vm.HasPlanningChildren = true;
|
|
Assert.True(vm.CanQueuePlan);
|
|
}
|
|
|
|
[Fact]
|
|
public void ActiveParentWithChildren_CannotQueuePlan()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle, phase: PlanningPhase.Active);
|
|
vm.HasPlanningChildren = true;
|
|
Assert.False(vm.CanQueuePlan);
|
|
}
|
|
|
|
[Fact]
|
|
public void FinalizedParentWithoutChildren_CannotQueuePlan()
|
|
{
|
|
var vm = MakeRow(TaskStatus.Idle, phase: PlanningPhase.Finalized);
|
|
Assert.False(vm.CanQueuePlan);
|
|
}
|
|
}
|