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