Reason#
Why the planner placed a task where it did. You read it from Mutation.Reason, which is set only when the call ran with Reasons = true in SolveOptions or PreviewOptions. You don't construct one yourself.
Declaration#
namespace Plen;
public abstract record Reason
{
public sealed record Asap(Id LinkId, Id SourceId) : Reason;
public sealed record Alap(Id LinkId, Id TargetId) : Reason;
public sealed record SummaryTranslation(Id SummaryId) : Reason;
public sealed record Constraint : Reason;
public sealed record Preferred : Reason;
}
Reason is a closed union: its constructor is private, so these five nested records are the only variants. Read a value with switch pattern matching on the nested types. The variants are immutable and compare by value.
Variants#
| Variant | Description |
|---|---|
Asap(LinkId, SourceId) |
A predecessor pushed the task later. LinkId is the driving link, SourceId the driving predecessor. |
Alap(LinkId, TargetId) |
A successor pulled the task earlier, in backward planning. LinkId is the driving link, TargetId the driving successor. |
SummaryTranslation(SummaryId) |
The task moved together with its summary. SummaryId is the summary that moved. |
Constraint |
The task's own constraint placed it. |
Preferred |
The task stayed at, or returned to, its own input date. |
How links, constraints and summaries decide a task's dates is covered in Links, constraints and summaries.
Example#
using Plen;
var monday = Moment.FromYmdHm(2026, 8, 10, 8, 0) ?? throw new InvalidOperationException("invalid date");
var data = new ProjectData
{
Tasks =
[
new TaskData(1) { StartDate = monday, Duration = 1.0 },
new TaskData(2) { StartDate = monday, Duration = 1.0 },
],
Links = [new LinkData { Id = 10, Source = 1, Target = 2 }],
};
var result = Engine.Schedule(data, new SolveOptions { Reasons = true });
foreach (var update in result.Updates)
{
var why = update.Reason switch
{
Reason.Asap r => $"pushed by task {r.SourceId} through link {r.LinkId}",
Reason.Alap r => $"pulled by task {r.TargetId} through link {r.LinkId}",
Reason.SummaryTranslation r => $"moved with summary {r.SummaryId}",
Reason.Constraint => "placed by its constraint",
Reason.Preferred => "kept at its own date",
_ => "no reason recorded",
};
Console.WriteLine($"task {update.Id}: {why}");
}
// task 2: pushed by task 1 through link 10
Related#
Mutation- the task update that carries the reasonSolveOptions- theReasonsswitch of a solvePreviewOptions- theReasonsswitch of a preview- Reading results - reading reasons from a result
- Links, constraints and summaries - how links, constraints and summaries place a task