[Canonical page](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# ScheduleResult

The output of one scheduling call: the tasks that changed, plus the cycles, conflicts and notes found on the way. [`Project.Solve`](<https://docs.dhtmlx.com/planning-engine/net/api/project-solve.md>), [`Project.Preview`](<https://docs.dhtmlx.com/planning-engine/net/api/project-preview.md>), [`Project.Analyze`](<https://docs.dhtmlx.com/planning-engine/net/api/project-analyze.md>) and [`Engine.Schedule`](<https://docs.dhtmlx.com/planning-engine/net/api/engine-schedule.md>) return one; [`Project.Level`](<https://docs.dhtmlx.com/planning-engine/net/api/project-level.md>) returns one in [`LevelingResult.Schedule`](<https://docs.dhtmlx.com/planning-engine/net/api/core/resources/leveling-result.md>). Cycles, conflicts and non-convergence are reported here as data, not thrown.

## Declaration

```csharp
namespace Plen;

public sealed class ScheduleResult : IDeepCloneable<ScheduleResult>, IEquatable<ScheduleResult>
{
    public string? BaselineId { get; set; }
    public List<Mutation> Updates { get; set; } = [];
    public List<Cycle> Cycles { get; set; } = [];
    public List<Conflict> Conflicts { get; set; } = [];
    public List<Note> Notes { get; set; } = [];
    public bool Converged { get; set; }
    public Analytics? Analytics { get; set; }
    public List<ResourceOverallocation>? Overallocations { get; set; }
    public LoadProfile? Profile { get; set; }

    public ScheduleResult DeepClone();
    public bool Equals(ScheduleResult? other);
}
```

A `ScheduleResult` is read-only by contract. The project keeps the result of [`Solve`](<https://docs.dhtmlx.com/planning-engine/net/api/project-solve.md>) and [`Level`](<https://docs.dhtmlx.com/planning-engine/net/api/project-level.md>) as its baseline, returns the same object from an unchanged re-solve, shares it with its clones, and reads it in [`Evaluate`](<https://docs.dhtmlx.com/planning-engine/net/api/project-evaluate.md>) and [`SuggestAssignments`](<https://docs.dhtmlx.com/planning-engine/net/api/project-suggest-assignments.md>). Call [`DeepClone`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result.md#deepclone>) before changing one.

## Properties

| Property | Type | Description |
|---|---|---|
| `BaselineId` | `string?` | Token of the baseline this result produced or read. `null` when there is none. |
| `Updates` | `List<`[`Mutation`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/mutation.md>)`>` | Tasks with new dates or fields. Only changed tasks unless [`IncludeUnchanged`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/solve-options.md#includeunchanged>) is set. |
| `Cycles` | `List<`[`Cycle`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result.md#cycle>)`>` | Every unique dependency cycle in the project. Empty for an acyclic project. |
| `Conflicts` | `List<`[`Conflict`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/conflict.md>)`>` | Constraint violations and calendar conflicts. |
| `Notes` | `List<`[`Note`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/note.md>)`>` | Non-fatal input notes: dropped links, tasks and assignments, and repairs. |
| `Converged` | `bool` | `true` when the planner reached a fixed point within the iteration limit. |
| `Analytics` | [`Analytics`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/analytics.md>)`?` | Critical path and slack. `null` unless the call's options request them. |
| `Overallocations` | `List<`[`ResourceOverallocation`](<https://docs.dhtmlx.com/planning-engine/net/api/core/resources/resource-overallocation.md>)`>?` | Overallocated resources. `null` when the project has no assignments. |
| `Profile` | [`LoadProfile`](<https://docs.dhtmlx.com/planning-engine/net/api/core/resources/load-profile.md>)`?` | Resource load of the previewed dates. Set only by [`Preview`](<https://docs.dhtmlx.com/planning-engine/net/api/project-preview.md>) with a `Profile` query. |

### BaselineId

```csharp
public string? BaselineId { get; set; }
```

The opaque token of the accepted baseline:

- [`Solve`](<https://docs.dhtmlx.com/planning-engine/net/api/project-solve.md>) and [`Level`](<https://docs.dhtmlx.com/planning-engine/net/api/project-level.md>) set the baseline they accepted.
- `Preview` sets the baseline it read.
- [`Analyze`](<https://docs.dhtmlx.com/planning-engine/net/api/project-analyze.md>) sets it only when called with `null` data on a solved project without unsolved edits.
- Otherwise it's `null`.

Pass the token as `BaselineId` in options and change sets to detect a stale baseline. Tokens are unique per process: don't store them or compare them across runs. With `IncludeUnchanged`, the extra entries in `Updates` don't change the token.

### Updates

```csharp
public List<Mutation> Updates { get; set; } = [];
```

One [`Mutation`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/mutation.md>) per reported task, carrying only the fields that changed. With [`SolveOptions.IncludeUnchanged`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/solve-options.md>), every scheduled task is reported. `Updates` is empty when:

- `Cycles` isn't empty and `IncludeUnchanged` is `false`, because a cyclic project keeps its dates
- the result comes from `Analyze`, which doesn't schedule

What the entries are relative to depends on the call; see [`Project.Preview`](<https://docs.dhtmlx.com/planning-engine/net/api/project-preview.md>) and [`Project.Level`](<https://docs.dhtmlx.com/planning-engine/net/api/project-level.md>).

### Cycles

```csharp
public List<Cycle> Cycles { get; set; } = [];
```

One [`Cycle`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result.md#cycle>) per unique simple dependency cycle. Rotations of the same cycle are reported once; two cycles that differ only in a parallel link are reported separately. Detection includes fixed and completed tasks and summary containment. When the list isn't empty, the call leaves all dates unchanged.

The number of cycles can grow exponentially in a densely cyclic graph.

### Conflicts

```csharp
public List<Conflict> Conflicts { get; set; } = [];
```

Constraint violations and calendar conflicts found by the planner. Always empty from `Analyze`. The variants are on the [`Conflict`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/conflict.md>) page.

### Notes

```csharp
public List<Note> Notes { get; set; } = [];
```

Input the engine ignored or fixed instead of rejecting. The variants are on the [`Note`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/note.md>) page.

### Converged

```csharp
public bool Converged { get; set; }
```

`false` when the planner stopped at [`SchedulingConfig.MaxIterations`](<https://docs.dhtmlx.com/planning-engine/net/api/core/data/scheduling-config.md>) before its dates settled. The result is still valid. Always `true` from `Analyze`.

### Analytics

```csharp
public Analytics? Analytics { get; set; }
```

`null` when neither `Slack` nor `CriticalPath` is set in the call's [`AnalyticsOptions`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/analytics-options.md>). Otherwise an [`Analytics`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/analytics.md>) value; check its `Status` before reading the data.

### Overallocations

```csharp
public List<ResourceOverallocation>? Overallocations { get; set; }
```

`null` when the project has no assignments. Otherwise one [`ResourceOverallocation`](<https://docs.dhtmlx.com/planning-engine/net/api/core/resources/resource-overallocation.md>) per overallocated resource; the list is empty when no resource is overallocated. Windows carry a `Reason` only in a result from `Level`.

### Profile

```csharp
public LoadProfile? Profile { get; set; }
```

Set only by [`Project.Preview`](<https://docs.dhtmlx.com/planning-engine/net/api/project-preview.md>) when [`PreviewOptions.Profile`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/preview-options.md>) is set; `null` otherwise. To read the load of the solved baseline, call [`Project.LoadProfile`](<https://docs.dhtmlx.com/planning-engine/net/api/project-load-profile.md>).

## Methods

### DeepClone

```csharp
public ScheduleResult DeepClone()
```

Returns a copy that shares no list or nested object with this one. Use it before changing a result.

### Equals

```csharp
public bool Equals(ScheduleResult? other)
public override bool Equals(object? obj)
```

Value equality: all properties are equal, with lists compared element by element. `GetHashCode` is consistent with it.

### ToString

```csharp
public override string ToString()
```

Returns a readable dump of all properties, for logging.

## Cycle

One dependency cycle: the tasks and links that form it.

```csharp
public sealed class Cycle : IDeepCloneable<Cycle>, IEquatable<Cycle>
{
    public List<Id> Tasks { get; set; } = [];
    public List<Id> Links { get; set; } = [];
}
```

| Property | Type | Description |
|---|---|---|
| `Tasks` | `List<`[`Id`](<https://docs.dhtmlx.com/planning-engine/net/api/core/data/id.md>)`>` | Ids of the tasks on the cycle. |
| `Links` | `List<`[`Id`](<https://docs.dhtmlx.com/planning-engine/net/api/core/data/id.md>)`>` | Ids of the links on the cycle. |

`Cycle` has value equality (both lists compared element by element), `DeepClone()` and `ToString()`, like `ScheduleResult`.

## Example

Solve a project with a dependency cycle and read the result:

```csharp
using Plen;

var monday = Moment.FromYmdHm(2026, 1, 5, 8, 0)
    ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks =
    [
        new TaskData("a") { StartDate = monday, Duration = 2.0 },
        new TaskData("b") { StartDate = monday, Duration = 3.0 },
    ],
    Links =
    [
        new LinkData { Id = "l1", Source = "a", Target = "b" },
        new LinkData { Id = "l2", Source = "b", Target = "a" },
    ],
};

var result = new Project().Solve(data, new SolveOptions());

foreach (var cycle in result.Cycles)
    Console.WriteLine($"cycle: tasks {string.Join(", ", cycle.Tasks)}; links {string.Join(", ", cycle.Links)}");

Console.WriteLine($"updates: {result.Updates.Count}, converged: {result.Converged}");

foreach (var update in result.Updates)
    Console.WriteLine($"{update.Id}: {update.StartDate} - {update.EndDate}");
```

## Related

- [`Project.Solve`](<https://docs.dhtmlx.com/planning-engine/net/api/project-solve.md>) - the call that returns a result and keeps it as the baseline
- [`Engine.Schedule`](<https://docs.dhtmlx.com/planning-engine/net/api/engine-schedule.md>) - the stateless call that returns one
- [`Mutation`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/mutation.md>) - one entry of `Updates`
- [`SolveOptions`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/solve-options.md>) - the switches that decide what a solve reports
- [Reading results](<https://docs.dhtmlx.com/planning-engine/net/scheduling/reading-results.md>) - writing updates back and handling each diagnostic
