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


# Project#suggestAssignments

Proposes resources for requirement slots of a [`Project`](<https://docs.dhtmlx.com/planning-engine/js/api/project.md>). It fills empty slots, repairs broken ones and rebalances valid ones, scoring each step like [`evaluate`](<https://docs.dhtmlx.com/planning-engine/js/api/project-evaluate.md>). The project isn't modified: the proposal comes back as a change set. Needs a solved project.

## Signature

```ts
suggestAssignments(slots: Slot[], options?: SuggestOptions): SuggestResult
```

Synchronous. Runs on the calling thread.

## Parameters

| Name | Type | Description |
|---|---|---|
| `slots` | [`Slot`](<https://docs.dhtmlx.com/planning-engine/js/api/types/slot.md>)`[]` | The slots to fill, repair or rebalance. To build them from skills and requirements, use [`slots`](<https://docs.dhtmlx.com/planning-engine/js/api/project-slots.md>); its output can be passed as is. |
| `options` | [`SuggestOptions`](<https://docs.dhtmlx.com/planning-engine/js/api/project-suggest-assignments.md#suggestoptions>) | Optional. Targets, fixed assignments, penalty, passes, scoring stage and baseline check. Default `{}` |

### SuggestOptions

Options for one `suggestAssignments` call. Every field is optional. Unknown keys are rejected.

| Field | Type | Default | Description |
|---|---|---|---|
| `baselineId` | `string` | no check | Expected baseline token, taken from the latest result's `baselineId`. A mismatch throws `ProjectError` `"stale-baseline"`. |
| `targets` | `("empty" \| "broken" \| "valid")[]` | `["empty"]` | Slot categories to act on, in any combination: `"empty"` fills slots with no assignment, `"broken"` repairs slots held by a resource outside `allowed`, `"valid"` rebalances slots held by a resource in `allowed`. `[]` changes no assignment and only reports `invalid`. Each value may appear once. |
| `fixed` | [`Id`](<https://docs.dhtmlx.com/planning-engine/js/api/types/common.md#id>)`[]` | `[]` | Assignment ids that are never changed. A slot bound to one of them is never visited; its resource still counts as in use, and the slot is still reported in `invalid` when broken. An id bound to no slot is accepted. |
| `newResourcePenalty` | `number` | `0` | Cost added per counted resource in use that the reference state doesn't use. `0` scores pure balance; a large value favors the fewest people. Included in `proposed.cost` and in each step's `cost`. Must be finite and `>= 0`. |
| `level` | [`LevelingOptions`](<https://docs.dhtmlx.com/planning-engine/js/api/types/leveling-options.md>) `\| false` | `false` | Leveling options: every candidate is leveled with them and scored on the leveled stage, and `proposed.leveled` is filled. `false` scores the scheduled stage. |
| `passes` | `number` | `1` | Maximum number of rebalance passes over `"valid"` slots. No effect unless `targets` contains `"valid"`. Must be `>= 1`. |
| `clearFirst` | `boolean` | `false` | `true` builds and measures the proposal on a copy of the accepted state with `leveling_delay` and `pauses` cleared on the tasks in scope (`level.scope.tasks`, otherwise every task). The clears are returned in `changes.tasks.update`. Independent of the `clearFirst` of `level`. |

For how targets, passes and the penalty drive the search, see [Suggesting assignments](<https://docs.dhtmlx.com/planning-engine/js/resources-and-assignment/suggesting-assignments.md>).

## Behavior

- Greedy: slots are visited one at a time, and later slots build on earlier picks.
- It never removes an assignment. It adds one per filled slot and changes the resource of a repaired or rebalanced one. A new assignment takes the slot's `id`.
- Commit the proposal with [`apply(result.changes)`](<https://docs.dhtmlx.com/planning-engine/js/api/project-apply.md>), then [`solve(null)`](<https://docs.dhtmlx.com/planning-engine/js/api/project-solve.md>), or [`level`](<https://docs.dhtmlx.com/planning-engine/js/api/project-level.md>) with the same leveling options when `level` was set.
- `slots` and `options` are converted like every other input; see [Plain objects and dates](<https://docs.dhtmlx.com/planning-engine/js/plain-objects-and-dates.md>).

## Returns

A `SuggestResult` with the proposed changes and a step log, a fresh object that shares nothing with the input or the project. With `dates: "date"` on the project, the finish dates in `proposed.scheduled` and `proposed.leveled` are local `Date` objects.

### SuggestResult

The proposal, scored against the accepted state. With `clearFirst`, `baseline` and `proposed` describe the cleared state, while `baselineId` still names the accepted state.

| Field | Type | Description |
|---|---|---|
| `baselineId` | `string` | Baseline token of the accepted state the proposal was scored against. |
| `changes` | [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/js/api/types/scheduling-changes.md>) | Net difference between the accepted state and the proposal, ready for `apply`. See below. |
| `steps` | [`SuggestStep`](<https://docs.dhtmlx.com/planning-engine/js/api/project-suggest-assignments.md#suggeststep>)`[]` | Decisions that changed the working state, in the order they were made. Empty when nothing moved. |
| `unfilled` | [`Id`](<https://docs.dhtmlx.com/planning-engine/js/api/types/common.md#id>)`[]` | Ids of targeted empty slots left empty, in visit order. Empty slots that weren't targeted aren't listed. |
| `invalid` | `Id[]` | Ids of slots whose final resource isn't in their `allowed` list, in visit order. Includes fixed slots and is filled whatever the `targets`. |
| `baseline` | `{ cost: number; resources: number }` | Cost and number of resources in use of the reference state on the scoring stage (leveled when `level` is set). |
| `proposed` | `{ cost: number; resources: number; scheduled: Metrics; leveled?: LeveledMetrics }` | Final state: cost including the penalty, resources in use, and [`Metrics`](<https://docs.dhtmlx.com/planning-engine/js/api/types/metrics.md>) against the reference state. `leveled` is present only when `level` is set. |

`changes` holds:

- `baselineId` equal to the result's `baselineId`.
- One assignment add `{ id, task, resource, value }` per filled slot, with the slot's id. A slot filled and then moved in a later pass is one add with its final resource.
- One assignment update `{ id, resource }` per repaired or rebalanced assignment, however many passes moved it. An assignment that ends on its original resource produces no entry.
- With `clearFirst`, one task update `{ id, leveling_delay: 0, pauses: [] }` per cleared task, ahead of the assignment edits, whenever something was cleared.

Empty buckets are left out, so with no steps and no clears `changes` is `{ baselineId }` alone.

### SuggestStep

One decision that changed the working state.

| Field | Type | Description |
|---|---|---|
| `slot` | `Id` | The slot's id. |
| `task` | `Id` | The slot's task. |
| `pass` | `number` | `0` for a fill or a repair, `1` and up for the rebalance pass that made the move. |
| `fromResource` | `Id \| null` | The previous resource; `null` for a fill. |
| `toResource` | `Id` | The new resource. |
| `cost` | `{ before: number; after: number }` | State cost including the penalty, on the scoring stage, before and after this step. The first step's `before` equals `baseline.cost`. |

## Errors

| Error | When |
|---|---|
| [`ProjectError`](<https://docs.dhtmlx.com/planning-engine/js/api/types/errors.md>) `"no-baseline"` | The project is unsolved, including after `apply`. |
| `ProjectError` `"stale-baseline"` | `options.baselineId` doesn't name the current baseline. |
| [`InputError`](<https://docs.dhtmlx.com/planning-engine/js/api/types/errors.md>) `"validation"` | Invalid options (`newResourcePenalty`, `passes`, `targets[i]`, `fixed[i]`, `level`); a cyclic accepted state (`path: null`); an invalid slot (paths under `slots[i]`: `.id`, `.task`, `.allowed[j]`, `.value`, `.assignment`). |
| `InputError` `"parse"` | An unknown key in `options` or in a slot. |
| `TypeError` | The input has a circular reference or a `BigInt`. |
| `Error("Project has been disposed")` | The project was disposed. |

Options are checked before slots. The accepted slot values are on the [`Slot`](<https://docs.dhtmlx.com/planning-engine/js/api/types/slot.md>) page.

## Example

Fill an open position on task `C`, then commit the proposal.

```js
import { Project } from "plen-js";

const project = new Project();
project.solve({
  config: { use_calendars: false },
  tasks: [
    { id: "A", start_date: "2026-08-10 00:00", duration: 2 },
    { id: "C", start_date: "2026-08-10 00:00", duration: 2 },
  ],
  resources: [{ id: "dev1" }, { id: "dev2" }],
  assignments: [{ id: "aA", task: "A", resource: "dev1", value: 1 }],
});

// Task C needs one person: dev1 or dev2.
const slots = [{ id: "sC", task: "C", allowed: ["dev1", "dev2"] }];
const result = project.suggestAssignments(slots);
for (const step of result.steps) {
  console.log(`${step.slot} -> ${step.toResource}`); // sC -> dev2
}

const report = project.apply(result.changes);
if (report.rejected.length === 0) {
  project.solve(null);
}
```

## Related

- [`Slot`](<https://docs.dhtmlx.com/planning-engine/js/api/types/slot.md>) - the input slots
- [`Project#slots`](<https://docs.dhtmlx.com/planning-engine/js/api/project-slots.md>) - building slots from skills
- [`Metrics`](<https://docs.dhtmlx.com/planning-engine/js/api/types/metrics.md>) - the scores in `proposed`
- [Suggesting assignments](<https://docs.dhtmlx.com/planning-engine/js/resources-and-assignment/suggesting-assignments.md>) - targets, picks and committing
- [Skills and Requirements](<https://docs.dhtmlx.com/planning-engine/js/resources-and-assignment/skills-and-requirements.md>) - from skills to suggestions
