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


# SuggestOptions

Options for [`Project.SuggestAssignments`](<https://docs.dhtmlx.com/planning-engine/net/api/project-suggest-assignments.md>). They set which baseline the call expects, which slot categories it acts on, which assignments stay as they are, the cost of each extra person, the scoring stage, the number of rebalance passes, and whether the call works on a copy with leveling delays cleared. `new SuggestOptions()` fills empty slots only, scored on the scheduled stage, with no penalty and one pass.

## Declaration

```csharp
public sealed class SuggestOptions : IDeepCloneable<SuggestOptions>
{
    public string? BaselineId { get; set; }
    public List<AssignTarget> Targets { get; set; } = [AssignTarget.Empty];
    public List<Id> Fixed { get; set; } = [];
    public double NewResourcePenalty { get; set; } = 0.0;
    public LevelingOptions? Level { get; set; }
    public uint Passes { get; set; } = 1;
    public bool ClearFirst { get; set; }

    public SuggestOptions DeepClone();
}
```

Set the properties you need with an object initializer: `new SuggestOptions { Targets = [AssignTarget.Empty, AssignTarget.Broken] }`. `SuggestOptions` doesn't implement `IEquatable<T>`, so two instances compare by reference.

## Properties

| Property | Type | Default | Description |
|---|---|---|---|
| `BaselineId` | `string?` | `null` | The baseline the call expects. `null` skips the check. |
| `Targets` | `List<`[`AssignTarget`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-options.md#assigntarget>)`>` | `[AssignTarget.Empty]` | Slot categories to act on. `[]` changes no assignment. |
| `Fixed` | `List<`[`Id`](<https://docs.dhtmlx.com/planning-engine/net/api/core/data/id.md>)`>` | empty | Assignment ids the call never changes. |
| `NewResourcePenalty` | `double` | `0.0` | Cost per extra person in use. Must be finite and `>= 0`. |
| `Level` | [`LevelingOptions`](<https://docs.dhtmlx.com/planning-engine/net/api/core/resources/leveling-options.md>)`?` | `null` | Scores every candidate on the leveled stage. `null` scores the scheduled stage. |
| `Passes` | `uint` | `1` | Rebalance passes over `Valid` slots. Must be `>= 1`. |
| `ClearFirst` | `bool` | `false` | Works on a copy with in-scope `LevelingDelay` / `Pauses` cleared. |

### BaselineId

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

A baseline token taken from the latest [`ScheduleResult.BaselineId`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result.md#baselineid>). When it isn't `null` and doesn't name the project's current baseline, the call throws a [`ProjectException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/project-exception.md>) with `Code ==` [`ProjectError.StaleBaseline`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/project-exception.md#projecterror>). [`SuggestResult.BaselineId`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-result.md>) and `SuggestResult.Changes.BaselineId` always hold the accepted state's token.

### Targets

```csharp
public List<AssignTarget> Targets { get; set; } = [AssignTarget.Empty];
```

The slot categories the call acts on, in any combination. Each category may appear once; a duplicate throws an [`InputException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/input-exception.md>) with `Path` `targets[i]`. Pass 0 handles `Empty` and `Broken` slots, and passes 1 to `Passes` handle `Valid` slots.

With `Targets = []`, the call changes no assignment and only reports [`SuggestResult.Invalid`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-result.md>), so it works as a check. `SuggestResult.Unfilled` lists only targeted empty slots.

### Fixed

```csharp
public List<Id> Fixed { get; set; } = [];
```

Assignment ids the call never changes. A slot bound to a listed assignment is skipped in every pass, but it still appears in `SuggestResult.Invalid` when its resource is outside its `Allowed` list.

Each id must name an active assignment and appear once. An unknown, inactive or repeated id throws an [`InputException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/input-exception.md>) with `Path` `fixed[i]`. An id that no slot is bound to is accepted.

### NewResourcePenalty

```csharp
public double NewResourcePenalty { get; set; } = 0.0;
```

The cost added for each resource from the slots' pool that is in use in the proposal but not in the reference state. It's included in every step's [`StepCost`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-result.md#stepcost>) and in [`SuggestProposal.Cost`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-result.md#suggestproposal>). `0` balances load only. A large value uses the fewest people.

A negative or non-finite value throws an [`InputException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/input-exception.md>) with `Path` `newResourcePenalty`.

### Level

```csharp
public LevelingOptions? Level { get; set; }
```

When set, each candidate is leveled with these options and scored on the leveled stage, and [`SuggestProposal.Leveled`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-result.md#suggestproposal>) holds the leveled metrics. When `null`, candidates are scored on the scheduled stage. `Level.Scope.Tasks` also sets the scope of `ClearFirst`.

Options that are invalid against the accepted state throw an [`InputException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/input-exception.md>). The proposal carries no leveling delays: to commit it, pass `SuggestResult.Changes` to [`Project.Apply`](<https://docs.dhtmlx.com/planning-engine/net/api/project-apply.md>), then call [`Project.Level`](<https://docs.dhtmlx.com/planning-engine/net/api/project-level.md>) with the same `LevelingOptions`.

### Passes

```csharp
public uint Passes { get; set; } = 1;
```

The maximum number of rebalance passes over `Valid` slots. A pass that moves nothing ends the loop early. It has no effect unless `Targets` contains `AssignTarget.Valid`. The pass number of each rebalance step is in [`SuggestStep.Pass`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-result.md#suggeststep>).

`Passes = 0` throws an [`InputException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/input-exception.md>) with `Path` `passes`.

### ClearFirst

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

When `true`, the reference state is a solved copy of the accepted state with `LevelingDelay` and `Pauses` cleared on the tasks in scope. The scope is `Level.Scope.Tasks` when `Level` gives it, otherwise every task, also when `Level` is `null`. `ClearFirst` doesn't depend on `Level.ClearFirst`.

[`SuggestResult.Changes`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-result.md>) then starts with one task update per cleared task, before the assignment edits, so applying it reproduces the cleared state. These updates are present whenever something was cleared, also with `Targets = []` or no slots.

`ClearFirst` adds no error: `BaselineId`, `Fixed`, `Level` and the slots are checked against the accepted state before anything is cleared. When nothing in scope has a delay or a pause, the result is the same as with `ClearFirst = false`.

## Methods

### DeepClone

```csharp
public SuggestOptions DeepClone()
```

Returns a copy that shares no nested object (`Targets`, `Fixed`, `Level`) with this one.

### ToString

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

Returns a debug dump of the fields, e.g. `SuggestOptions { BaselineId: …, Targets: …, Fixed: …, NewResourcePenalty: …, Level: …, Passes: …, ClearFirst: … }`.

## AssignTarget

A slot category for `Targets`. The category of each slot is judged from its current resource in the accepted state.

```csharp
public enum AssignTarget
{
    Empty,
    Broken,
    Valid,
}
```

| Value | Description |
|---|---|
| `Empty` | The slot has no assignment. Filled in pass 0. |
| `Broken` | The slot's resource isn't in its `Allowed` list. Moved to the cheapest allowed candidate in pass 0. |
| `Valid` | The slot's resource is in its `Allowed` list. Rebalanced in passes 1 to `Passes`, and moved only when that lowers the cost. |

## Example

```csharp
using Plen;

var monday = Moment.FromYmdHm(2026, 8, 10, 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 = 2.0 },
    ],
    Resources = [new ResourceData { Id = "dev1" }, new ResourceData { Id = "dev2" }],
    Assignments =
    [
        new AssignmentData { Id = "aA", Task = "A", Resource = "dev1", Value = 1.0 },
    ],
};
var project = new Project();
var solved = project.Solve(data, new SolveOptions());

var slots = new List<Slot>
{
    new Slot("aA", "A", ["dev1", "dev2"]) { Assignment = "aA" },
    new Slot("sB", "B", ["dev1", "dev2"]),
};
var options = new SuggestOptions
{
    BaselineId = solved.BaselineId,
    Targets = [AssignTarget.Empty, AssignTarget.Valid],
    Fixed = ["aA"],
    NewResourcePenalty = 0.5,
};
var result = project.SuggestAssignments(slots, options);

foreach (var step in result.Steps)
    Console.WriteLine($"{step.Slot}: {step.FromResource} -> {step.ToResource}");
```

## Related

- [`Project.SuggestAssignments`](<https://docs.dhtmlx.com/planning-engine/net/api/project-suggest-assignments.md>) - the call these options configure
- [`SuggestResult`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-result.md>) - the proposal it returns
- [`Slot`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/slot.md>) - the slots the targets refer to
- [`LevelingOptions`](<https://docs.dhtmlx.com/planning-engine/net/api/core/resources/leveling-options.md>) - the options of the leveled scoring stage
- [Suggesting assignments](<https://docs.dhtmlx.com/planning-engine/net/resources-and-assignment/suggesting-assignments.md>) - how picks are made and how to read the proposal
