[Canonical page](<https://docs.dhtmlx.com/planning-engine/rust/how-to/reschedule-a-branch/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Reschedule a branch

Reschedule one task and the work that depends on it, and leave the rest of the project where it is. This is the same as `gantt.autoSchedule(id, inclusive)` in dhtmlxGantt.

The scope is the task you name, its summary ancestors and descendants, and its successors through links. Tasks outside the scope keep their dates.

<!- TODO: the cheatsheet says `inclusive` only decides whether the root task is scheduled; planner compute_scope() also walks predecessor links when inclusive. Confirm the exact scope rule. -->

## Steps

1. Build a [`Subtree`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/subtree.md>) for the task the branch starts from. [`Subtree::new`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/subtree.md#new>) sets `inclusive: true`, so the root task is scheduled too:

   ```rust
   use plen_core::{SolveOptions, Subtree};

   let options = SolveOptions {
       subtree: Some(Subtree::new("design")),
       ..Default::default()
   };
   ```

   To leave the root task itself out, set `inclusive: false`:

   ```rust
   let subtree = Subtree { task: "design".into(), inclusive: false };
   ```

2. Pass the options to [`Project::solve`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>). Use `Some(data)` on the first load, or `None` to reschedule the current state:

   ```rust
   let result = project.solve(None, &options)?;
   ```

3. Write the entries of `result.updates` back to your model. Each entry is a task in the scope that moved. The scoped dates become the accepted baseline.

The full example below has two independent chains, `backend → api` and `design → ui`. Both successors start too early. The scoped solve fixes only the `design` branch:

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

fn task(id: &str, start: DateTime, duration: f64) -> TaskData {
    let mut t = TaskData::new(id);
    t.start_date = Some(start);
    t.duration = Some(duration);
    t
}

fn link(id: &str, source: &str, target: &str) -> LinkData {
    LinkData {
        id: id.into(),
        source: source.into(),
        target: target.into(),
        link_type: LinkType::Fs,
        lag: 0.0,
        lag_unit: None,
    }
}

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

    let mut data = ProjectData {
        tasks: vec![
            task("backend", jan1, 2.0),
            task("api", jan1, 1.0),
            task("design", jan1, 3.0),
            task("ui", jan1, 1.0),
        ],
        links: vec![link("l1", "backend", "api"), link("l2", "design", "ui")],
        ..Default::default()
    };
    // 24/7 time keeps the example dates easy to check.
    data.config.use_calendars = false;

    let options = SolveOptions {
        subtree: Some(Subtree::new("design")),
        ..Default::default()
    };

    let mut project = Project::default();
    let result = project.solve(Some(data), &options)?;

    for m in &result.updates {
        if let (Some(start), Some(end)) = (m.start_date, m.end_date) {
            println!("{} {}..{}", m.id, start, end);
        }
    }
    Ok(())
}
```

## Preview a branch

To see what rescheduling a branch would do without changing the project, set the same `subtree` on [`PreviewOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/preview-options.md>) and call [`Project::preview`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>):

```rust
use plen_core::{PreviewOptions, SchedulingChanges, Subtree};

let preview = project.preview(
    &SchedulingChanges::default(),
    &PreviewOptions {
        subtree: Some(Subtree::new("backend")),
        ..Default::default()
    },
)?;
```

The preview scope applies to that call only. It doesn't touch the baseline or the cached solve result. A preview scope can also name a task that the same `changes` add.

## Things to know

- Tasks outside the scope don't move until you solve them. In the example, the next `project.solve(None, &SolveOptions::default())` moves only `api`, because `ui` is already accepted.
- The result cache is keyed on the whole [`SolveOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/solve-options.md>). A different `subtree` replans from the accepted dates. The same `subtree` again returns the cached result.
- With [`include_unchanged`](<https://docs.dhtmlx.com/planning-engine/rust/how-to/load-all-task-dates.md>), a scoped solve reports every task in the scope, plus the out-of-scope tasks a sparse scoped solve would report.
- On a project with a dependency cycle, nothing is scheduled, scoped or not. Check `result.cycles`.
- An unknown or unscheduled task in `subtree.task` fails with [`Error::Input`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>) at path `subtree.task`, message `unknown or unscheduled scope task`. The project keeps its previous state and cached result. See [Error handling](<https://docs.dhtmlx.com/planning-engine/rust/error-handling.md>).

## Result

The example prints one line. Only `ui` moves, to start after `design` ends. `api` still overlaps `backend`:

```text
ui 2026-01-04 00:00..2026-01-05 00:00
```

Without `subtree`, the same solve also moves `api` to `2026-01-03 00:00`.
