Conflict#
A scheduling conflict found while placing tasks. It isn't built by hand: read it from ScheduleResult.Conflicts, which lists every conflict of the run. A conflict is data in the result, not an exception. The run still succeeds and returns dates.
Declaration#
namespace Plen;
public abstract record Conflict
{
public sealed record ConstraintViolation(
Id Task, ConstraintType Constraint, Violation Violation, Moment Required, Moment Actual) : Conflict;
public sealed record CalendarNonWorking(Id Task, Moment Proposed, Moment Snapped) : Conflict;
}
Conflict has a private constructor, so these two nested records are its only variants. Read them with switch pattern matching. Each variant is an immutable positional record with value equality and a generated ToString(), such as ConstraintViolation { Task = demo, Constraint = Snlt, Violation = LatestStart, Required = 2026-03-03 00:00, Actual = 2026-03-05 00:00 }.
Variants#
| Variant | When it occurs |
|---|---|
ConstraintViolation |
A task's constraint placed it at a date its driving dependency link doesn't allow. The constraint wins, and the task keeps the constraint's date. Reported only when SchedulingConfig.ApplyConstraints is true. |
CalendarNonWorking |
A summary moved as one block, and a descendant task landed on non-working time of its calendar. Reported only when SchedulingConfig.StrictCalendar is true. |
ConstraintViolation#
| Property | Type | Description |
|---|---|---|
Task |
Id |
The task or summary whose constraint won. |
Constraint |
ConstraintType |
The constraint that placed the task: the task's own, or one inherited from a summary. |
Violation |
Violation |
Which bound the link contradicts. |
Required |
Moment |
The date the constraint gave the task. The task has this date in the result. |
Actual |
Moment |
The date the driving link would have given the task. |
CalendarNonWorking#
| Property | Type | Description |
|---|---|---|
Task |
Id |
The descendant task that landed on non-working time. |
Proposed |
Moment |
The task's new start, or its new end when the project is scheduled from the end. The date is on non-working time. |
Snapped |
Moment |
The date the task keeps. Currently always equal to Proposed: the task isn't moved to working time. |
Violation#
Which bound of a constraint the driving link contradicts, in Conflict.ConstraintViolation.Violation. EarliestStart and LatestStart compare start dates; EarliestEnd and LatestEnd compare end dates.
namespace Plen;
public enum Violation
{
EarliestStart,
LatestStart,
EarliestEnd,
LatestEnd,
}
| Value | Description |
|---|---|
EarliestStart |
The link wants an earlier start than the constraint allows. |
LatestStart |
The link wants a later start than the constraint allows. |
EarliestEnd |
The link wants an earlier end than the constraint allows. |
LatestEnd |
The link wants a later end than the constraint allows. |
Example#
demo must start no later than Tuesday, but its predecessor build ends on Wednesday. The constraint wins, and the result reports the conflict:
using Plen;
var monday = Moment.FromYmdHm(2026, 3, 2, 0, 0)!.Value;
var tuesday = Moment.FromYmdHm(2026, 3, 3, 0, 0)!.Value;
var data = new ProjectData
{
Config = new SchedulingConfig { ApplyConstraints = true },
Tasks =
[
new TaskData("build") { StartDate = monday, Duration = 3 },
new TaskData("demo")
{
StartDate = tuesday,
Duration = 1,
ConstraintType = ConstraintType.Snlt,
ConstraintDate = tuesday,
},
],
Links = [new LinkData { Id = 1, Source = "build", Target = "demo" }],
};
var result = Engine.Schedule(data, new SolveOptions());
foreach (var conflict in result.Conflicts)
{
var text = conflict switch
{
Conflict.ConstraintViolation v =>
$"{v.Task}: {v.Constraint} {v.Violation}, constraint gives {v.Required}, link wants {v.Actual}",
Conflict.CalendarNonWorking n =>
$"{n.Task}: on non-working time at {n.Proposed}",
_ => throw new InvalidOperationException("unknown conflict"),
};
Console.WriteLine(text);
}
// demo: Snlt LatestStart, constraint gives 2026-03-03 00:00, link wants 2026-03-05 00:00
The _ arm only satisfies the compiler's exhaustiveness check; no other variant exists.
Related#
ScheduleResult-Conflictslists every conflict of the runSchedulingConfig-ApplyConstraintsandStrictCalendarturn the conflicts onTaskData- task constraints and their types- Reading results - reading conflicts in a result
- Links, constraints and summaries - detecting a constraint that a link breaks