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


# SkillLayer.Slots

Turns a [`Pool`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/pool.md>) of workers and a list of [`Requirement`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/requirement.md>)s into the [`Slot`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/slot.md>)s that [`Project.SuggestAssignments`](<https://docs.dhtmlx.com/planning-engine/net/api/project-suggest-assignments.md>) takes. Existing active assignments are bound to the slots, so repeated runs stay stable after a commit. Part of the `Plen.Assign` package (namespace [`Plen.Assign`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/overview.md>)).

```bash
dotnet add package Plen.Assign
```

## Signature

```csharp
public static List<Slot> Slots(Project project, Pool pool, IReadOnlyList<Requirement> requirements)
```

Synchronous and CPU-only. Reads the project's accepted state through [`Project.Roster`](<https://docs.dhtmlx.com/planning-engine/net/api/project-roster.md>) and never modifies the project.

## Parameters

| Name | Type | Description |
|---|---|---|
| `project` | [`Project`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/project.md>) | A solved project with no unsolved edits. Tasks, resources and assignments are checked against its accepted state. |
| `pool` | [`Pool`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/pool.md>) | Workers that may be assigned. Pool order sets the order of each slot's `Allowed`. |
| `requirements` | `IReadOnlyList<`[`Requirement`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/requirement.md>)`>` | What each task needs. An empty list returns an empty list. |

## Returns

A new `List<Slot>`: `Count` slots for each requirement, in requirement order, then instance order. Slot properties:

| Property | Value |
|---|---|
| `Id` | `"<requirement id>#<n>"`, `n` = 1..`Count` (for example `be#1`, `be#2`) |
| `Task` | The requirement's `Task` |
| `Allowed` | Pool workers for which every [`SkillMatch`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/requirement.md#skillmatch>) of the requirement holds, in pool order. An empty `Matches` list allows every worker. |
| `Value` | The requirement's `Units` |
| `Assignment` | The id of the existing active assignment bound to this slot, or `null` |

On the next call, an assignment created from a slot binds back to that slot by id. That's why a requirement id must stay on its task; see [`Requirement.Id`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/requirement.md#id>).

[`SuggestAssignments`](<https://docs.dhtmlx.com/planning-engine/net/api/project-suggest-assignments.md>) takes the result as is.

## Binding

The task's existing active assignments are bound per task, across all requirements on that task, in three steps:

1. **Slot id.** An assignment whose id is one of the requirement's slot ids binds to that slot, even when its resource isn't in `Allowed`. The slot is then broken ([`AssignTarget.Broken`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-options.md#assigntarget>)). This step runs for every requirement of the task before any explicit list.
2. **Explicit list.** [`Requirement.Assignments`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/requirement.md#assignments>): a non-null list replaces automatic binding for that requirement. A listed slot-id assignment keeps its slot, the other entries take the remaining slots in order, and leftover slots stay empty. An empty list leaves every slot empty.
3. **Matching.** A deterministic maximum bipartite matching binds the remaining assignments to the empty slots of requirements without a list. An assignment can take a slot only when its resource is in the slot's `Allowed`. Input order decides between equal matchings, and an earlier requirement can't take the only worker a later one fits.

Assignments left unbound stay on the task without a slot.

## Errors

Checks run in this order, and the first failure throws: the project state, then the pool, then each requirement's fields in input order, then binding.

| Error | When |
|---|---|
| [`ProjectException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/project-exception.md>) `NoBaseline` | The project is unsolved or has unsolved edits (after [`Apply`](<https://docs.dhtmlx.com/planning-engine/net/api/project-apply.md>)). |
| [`InputException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/input-exception.md>) (`Kind = Validation`) | The pool or a requirement is invalid. `Path` names the item; see the table below. |
| `ArgumentNullException` | `project`, `pool` or `requirements` is `null`. |

`InputException.Path` values:

| Path | When |
|---|---|
| `pool.workers[i].resource` | Unknown resource, or the same resource listed as two workers |
| `pool.workers[i].skills[j].id` | The same skill listed twice on one worker |
| `pool.workers[i].skills[j].value` | Not finite |
| `requirements[i].id` | Duplicate requirement id, or one of its slot ids is an active assignment on another task |
| `requirements[i].task` | Unknown task, or not a scheduled leaf task |
| `requirements[i].count` | `0` |
| `requirements[i].units` | Not finite or `<= 0` |
| `requirements[i].matches[j].value` | Not finite |
| `requirements[i].assignments` | More entries than `Count`, or an assignment with one of this requirement's slot ids left out of the list |
| `requirements[i].assignments[k]` | Unknown or inactive assignment, an assignment on another task, or one bound twice (the message names the other requirement when it already holds it) |

Both exceptions derive from [`PlenException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/plen-exception.md>), so one `catch (PlenException)` covers this call and the rest of the API. The method takes no baseline id, so it never throws [`StaleBaseline`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/project-exception.md#projecterror>).

## Example

```csharp
using Plen;
using Plen.Assign;

var monday = Moment.FromYmdHm(2026, 8, 10, 8, 0) ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks = [new TaskData("B") { StartDate = monday, Duration = 2 }],
    Resources = [new ResourceData { Id = "ann" }, new ResourceData { Id = "bob" }],
};
var project = new Project();
project.Solve(data, new SolveOptions());

var pool = new Pool
{
    Workers =
    [
        new Worker { Resource = "ann", Skills = [new Skill { Id = "backend", Value = 1 }] },
        new Worker { Resource = "bob", Skills = [new Skill { Id = "backend", Value = 2 }] },
    ],
};
List<Requirement> requirements = [new("be", "B", [SkillMatch.AtLeast("backend", 2)])];

var slots = SkillLayer.Slots(project, pool, requirements);
// One slot "be#1" on task "B", Allowed = [bob]

var proposal = project.SuggestAssignments(slots, new SuggestOptions());
project.Apply(proposal.Changes);
project.Solve(null, new SolveOptions());
```

## Related

- [`Pool`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/pool.md>) - the workers
- [`Requirement`](<https://docs.dhtmlx.com/planning-engine/net/api/assign/requirement.md>) - what each task needs
- [`Slot`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/slot.md>) - the returned slots
- [`Project.SuggestAssignments`](<https://docs.dhtmlx.com/planning-engine/net/api/project-suggest-assignments.md>) - the call that takes the slots
- [Skills and requirements](<https://docs.dhtmlx.com/planning-engine/net/resources-and-assignment/skills-and-requirements.md>) - the full workflow
