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


# Mutation

The new values of one task after a scheduling call. Each entry in [`ScheduleResult::updates`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md#updates>) is a `Mutation`. A field is `Some` only when the result reports it. The rest are `None`.

Which tasks get an entry, and what the values are compared against, is described on [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md>).

## Declaration

```rust
#[derive(Clone, Debug, PartialEq)]
pub struct Mutation {
    pub id: Id,
    pub kind: MutationKind,
    pub start_date: Option<DateTime>,
    pub end_date: Option<DateTime>,
    pub duration: Option<f64>,
    pub leveling_delay: Option<f64>,
    pub pauses: Option<Vec<Pause>>,
    pub reason: Option<Reason>,
}
```

## Fields

The date and leveling fields have the same names and units as on [`TaskData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/task-data.md#fields>).

| Field | Type | Present when | Description |
|---|---|---|---|
| `id` | [`Id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/id.md>) | always | Id of the task. |
| `kind` | [`MutationKind`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/mutation-kind.md>) | always | Whether the task is a plain task, a summary or a milestone. |
| `start_date` | `Option<`[`DateTime`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/date-time.md>)`>` | the start changed, or the task is reported in full | New start. |
| `end_date` | `Option<`[`DateTime`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/date-time.md>)`>` | the end changed, or the task is reported in full | New end. |
| `duration` | `Option<f64>` | the duration changed, or the task is reported in full | New working time in the task's duration unit. |
| `leveling_delay` | `Option<f64>` | the leveling delay changed | New leveling delay, elapsed time in the task's duration unit. |
| `pauses` | `Option<Vec<`[`Pause`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/pause.md>)`>>` | the pause list changed | The complete new pause list. `Some(vec![])` when every pause was removed. A task that had no pauses and still has none never gets this field. |
| `reason` | `Option<`[`Reason`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/reason.md>)`>` | the call ran with `reasons: true` | What placed the task. `None` when no planner ran (a cyclic project). |

A task is reported in full when `include_unchanged` is set on [`SolveOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/solve-options.md>), or when an `apply` touched it since the last solve. Such an entry has `start_date`, `end_date` and `duration` all set, even if none of them changed.

## Writing back

The field names match [`TaskUpdate`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/task-update.md>): each `Some(v)` maps to [`Patch::Value(v)`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/patch.md>) on the field of the same name. `kind` and `reason` have no counterpart there.

With the `serde` feature, `Mutation` implements `Serialize` and `Deserialize`. Field names stay snake_case, `None` fields are left out, `kind` is kebab-case and `reason` is an object tagged by `kind`. A serialized entry decodes as a JSON task update.

## Example

```rust
use plen_core::{
    schedule, DateTime, LinkData, LinkType, ProjectData, SolveOptions, TaskData,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let monday = DateTime::from_ymd_hm(2026, 4, 6, 8, 0).ok_or("invalid date")?;

    let data = ProjectData {
        tasks: vec![
            TaskData {
                start_date: Some(monday),
                duration: Some(2.0),
                ..TaskData::new("design")
            },
            TaskData {
                start_date: Some(monday),
                duration: Some(3.0),
                ..TaskData::new("build")
            },
        ],
        links: vec![LinkData {
            id: "l1".into(),
            source: "design".into(),
            target: "build".into(),
            link_type: LinkType::Fs,
            lag: 0.0,
            lag_unit: None,
        }],
        ..Default::default()
    };

    let options = SolveOptions {
        reasons: true,
        ..Default::default()
    };
    let result = schedule(data, &options)?;

    for update in &result.updates {
        if let Some(start) = update.start_date {
            println!("{} starts {} ({:?})", update.id, start, update.reason);
        }
    }
    Ok(())
}
```

## Related

- [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md#updates>) - holds the update entries
- [`TaskUpdate`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/task-update.md>) - the change type an entry maps back to
- [`Reason`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/reason.md>) - the type of `reason`
- [Reading results](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/reading-results.md#updates>) - reads and applies update entries
- [Data Updates](<https://docs.dhtmlx.com/planning-engine/rust/live-projects/editing-a-project.md>) - feeds results back into a live project
