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


# SuggestResult

The proposal returned by [`Project::suggest_assignments`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#suggest_assignments>), scored against the accepted state. It holds the net change set, the steps that built it, the slots it couldn't fill or that are out of their `allowed` list, and the scores before and after. Commit the proposal with [`Project::apply`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#apply>).

## Declaration

```rust
#[derive(Clone, Debug)]
pub struct SuggestResult {
    pub baseline_id: String,
    pub changes: SchedulingChanges,
    pub steps: Vec<SuggestStep>,
    pub unfilled: Vec<Id>,
    pub invalid: Vec<Id>,
    pub baseline: AssignTotal,
    pub proposed: SuggestProposal,
}
```

With the `serde` feature, `SuggestResult` implements `Serialize` only. `changes` is written with unset fields and empty buckets left out, so the output decodes and applies as is.

## Fields

| Field | Type | Description |
|---|---|---|
| `baseline_id` | `String` | Baseline token of the accepted state the proposal was scored against. |
| `changes` | [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/scheduling-changes.md>) | Net difference between the accepted state and the proposal. See [`changes`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-result.md#changes>) below. |
| `steps` | `Vec<`[`SuggestStep`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-step.md>)`>` | Decisions that changed the working state, in the order they were made. Empty when nothing moved. |
| `unfilled` | `Vec<`[`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>)`>` | Ids of targeted empty slots left empty, in visit order. Empty slots that weren't targeted aren't listed. |
| `invalid` | `Vec<`[`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>)`>` | Ids of slots whose final resource isn't in their `allowed` list, in visit order. Includes fixed slots and is filled whatever the [`targets`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-options.md>). |
| `baseline` | [`AssignTotal`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/assign-total.md>) | Score of the reference state on the scoring stage (leveled when [`SuggestOptions::level`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-options.md>) is `Some`). |
| `proposed` | [`SuggestProposal`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-proposal.md>) | Score and metrics of the final working state. |

With [`SuggestOptions::clear_first`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-options.md#clear_first>), `baseline` and `proposed` describe the cleared state, while `baseline_id` still names the accepted state.

### changes

- `changes.baseline_id` is `Some(baseline_id)`.
- A filled slot becomes one assignment add `{id: slot id, task, resource, value}`. The new assignment takes the slot's id.
- A repaired or rebalanced assignment becomes one update `{id, resource}`, however many passes moved it. An assignment that ends on its original resource produces no entry.
- A slot filled and then moved in a later pass is one add with its final resource.
- With `clear_first`, one task update `{id, leveling_delay: 0, pauses: []}` per cleared task comes before the assignment edits. These clears are present whenever something was cleared, also when no slot moved.
- No leveling delays are included.

With no steps and no clears, `changes` carries only `baseline_id`.

Applying `changes` reproduces `proposed` under [`GapBehavior::Compress`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/gap-behavior.md>). For the commit flow and the preserve-mode caveat, see [Suggesting assignments](<https://docs.dhtmlx.com/planning-engine/rust/resources-and-assignment/suggesting-assignments.md>).

## Example

```rust
use plen_core::{Error, Project, SuggestResult};

// Report the proposal, then commit it.
fn commit(project: &mut Project, result: &SuggestResult) -> Result<(), Error> {
    for step in &result.steps {
        println!("{} -> {} (pass {})", step.slot, step.to_resource, step.pass);
    }
    if !result.unfilled.is_empty() {
        println!("unfilled: {:?}", result.unfilled);
    }
    if !result.invalid.is_empty() {
        println!("invalid: {:?}", result.invalid);
    }
    println!("cost {} -> {}", result.baseline.cost, result.proposed.cost);

    project.apply(&result.changes)?;
    Ok(())
}
```

## Related

- [`SuggestOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/suggest-options.md>) - the options that shape this result
- [`Slot`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/suggest/slot.md>) - the input whose ids appear in `unfilled`, `invalid` and the steps
- [`Error`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>) - returned instead of a result when the call fails
- [Suggesting assignments](<https://docs.dhtmlx.com/planning-engine/rust/resources-and-assignment/suggesting-assignments.md#reading-the-proposal>) - shows reading and committing the proposal
- [Serialize results to JSON](<https://docs.dhtmlx.com/planning-engine/rust/how-to/serialize-results-to-json.md>) - shows sending this result as JSON
