[Canonical page](<https://docs.dhtmlx.com/planning-engine/java/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/java/api/assign/pool.md>) of workers and a list of [`Requirement`](<https://docs.dhtmlx.com/planning-engine/java/api/assign/requirement.md>)s into the [`Slot`](<https://docs.dhtmlx.com/planning-engine/java/api/suggest/slot.md>)s that [`Project#suggestAssignments`](<https://docs.dhtmlx.com/planning-engine/java/api/project-suggest-assignments.md>) takes. Existing active assignments are bound to the slots, so repeated runs stay stable after a commit. Needs a solved project with no unsolved edits.

## Signature

```java
public static Vec<Slot> slots(Project project, Pool pool, Vec<Requirement> requirements)
```

Synchronous and CPU-only. Reads the project's accepted state through [`Project#roster`](<https://docs.dhtmlx.com/planning-engine/java/api/project-roster.md>) and never modifies the project. Not synchronized: don't call it while another thread uses the same `Project`.

## Parameters

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

## Returns

A new `Vec<Slot>`: for each requirement, `count` slots, in requirement order, then instance order. Slot fields:

| Field | Value |
|---|---|
| `id` | `Id.str("<requirement id>#<n>")`, `n` = 1..`count` (for example `be#1`, `be#2`), built by [`slotId`](<https://docs.dhtmlx.com/planning-engine/java/api/skill-layer-slots.md#skilllayerslotid>) |
| `task` | The requirement's `task` |
| `allowed` | Pool workers for which every [`SkillMatch`](<https://docs.dhtmlx.com/planning-engine/java/api/assign/requirement.md#skillmatch>) of the requirement holds, in pool order. No matches allows every worker |
| `value` | The requirement's `units` |
| `assignment` | 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. Keep each requirement id on the same task; see [`Requirement`](<https://docs.dhtmlx.com/planning-engine/java/api/assign/requirement.md>).

[`suggestAssignments`](<https://docs.dhtmlx.com/planning-engine/java/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/java/api/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/java/api/assign/requirement.md>) non-`null` 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 whose `assignments` is `null`. 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

All exceptions are unchecked. The first failing check wins: the project state, then the pool, then each requirement's fields in input order, then the slot limit, then binding per task.

| Error | When |
|---|---|
| [`MatchException.Project`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/match-exception.md>) | The project is unsolved or has unsolved edits, for example after [`apply`](<https://docs.dhtmlx.com/planning-engine/java/api/project-apply.md>). `code()` returns `ProjectError.NO_BASELINE`; `error` holds the [`ProjectException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/project-exception.md>) |
| [`MatchException.Invalid`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/match-exception.md>) | The pool or a requirement is invalid. `path` names the item, `detail` the reason |
| [`InputException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/input-exception.md>) (`VALIDATION`, `path` `null`) | The summed `count` of all requirements reaches `Integer.MAX_VALUE` |
| `OutOfMemoryError` | The summed `count` is below that limit but too large to allocate |
| `NullPointerException` | `project`, `pool` or `requirements` is `null` |

`MatchException.Invalid` paths:

| 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 or unscheduled 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 detail names the other requirement when it already holds the assignment) |

There's no `STALE_BASELINE`: the call takes no baseline token.

`MatchException` isn't a [`PlenException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/plen-exception.md>); `toPlenException()` converts it to the matching `InputException` or `ProjectException`. Its simple name clashes with `java.lang.MatchException`, so code that catches it needs `import org.dhtmlx.plen.MatchException;` next to `import org.dhtmlx.plen.*;`.

## Collection overload

```java
public static Vec<Slot> slots(Project project, Pool pool, java.util.Collection<Requirement> requirements)
```

Copies `requirements` into a [`Vec`](<https://docs.dhtmlx.com/planning-engine/java/api/support/vec.md>) and calls the main form, so `List.of(...)` works. Same behavior and errors.

## SkillLayer.slotId

```java
public static String slotId(Id requirement, long n)
```

Returns the id of a requirement's `n`-th slot: the requirement id's [`key()`](<https://docs.dhtmlx.com/planning-engine/java/api/data/id.md>), `#`, then `n` (`slotId(Id.str("be"), 2)` is `"be#2"`). `slots` uses it for every slot id, and an assignment created from a slot carries the same id. `n` isn't checked.

## SkillLayer.validatePool

```java
public static void validatePool(Roster roster, Pool pool)
```

Runs the pool checks of `slots` alone against a [`Roster`](<https://docs.dhtmlx.com/planning-engine/java/api/suggest/roster.md>) from [`Project#roster`](<https://docs.dhtmlx.com/planning-engine/java/api/project-roster.md>). Returns normally when the pool is valid.

Throws `MatchException.Invalid` with the `pool.workers[...]` paths listed under [Errors](<https://docs.dhtmlx.com/planning-engine/java/api/skill-layer-slots.md#errors>), and `NullPointerException` when `roster` or `pool` is `null`.

## Example

```java
import java.util.List;
import org.dhtmlx.plen.*;

public class SlotsExample {
  public static void main(String[] args) throws Exception {
    ProjectData data = new ProjectData()
        .withTasks(List.of(new TaskData(Id.str("B"))
            .withStartDate(Moment.fromYmdHm(2026, 8, 10, 8, 0))
            .withDuration(2.0)))
        .withResources(List.of(
            new ResourceData().withId(Id.str("ann")),
            new ResourceData().withId(Id.str("bob"))));

    Project project = new Project();
    project.solve(data, new SolveOptions());

    Pool pool = new Pool().withWorkers(List.of(
        new Worker().withResource(Id.str("ann"))
            .withSkills(List.of(new Skill().withId(Id.str("backend")).withValue(1.0))),
        new Worker().withResource(Id.str("bob"))
            .withSkills(List.of(new Skill().withId(Id.str("backend")).withValue(2.0)))));
    Requirement requirement = new Requirement(Id.str("be"), Id.str("B"),
        List.of(SkillMatch.atLeast(Id.str("backend"), 2.0)));

    Vec<Slot> slots = SkillLayer.slots(project, pool, List.of(requirement));
    Slot slot = slots.get(0);
    System.out.println(slot.id + " on " + slot.task + ", allowed " + slot.allowed);
    // be#1 on B, allowed [bob]

    SuggestResult proposal = project.suggestAssignments(slots, new SuggestOptions());
    project.apply(proposal.changes);
  }
}
```

For the full workflow, see [Skills and requirements](<https://docs.dhtmlx.com/planning-engine/java/resources-and-assignment/skills-and-requirements.md>).

## Related

- [`Pool`](<https://docs.dhtmlx.com/planning-engine/java/api/assign/pool.md>) - the workers that may be assigned
- [`Requirement`](<https://docs.dhtmlx.com/planning-engine/java/api/assign/requirement.md>) - what each task needs
- [`MatchException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/match-exception.md>) - the error it throws
- [`Project#suggestAssignments`](<https://docs.dhtmlx.com/planning-engine/java/api/project-suggest-assignments.md>) - fills the slots it builds
- [Skills and requirements](<https://docs.dhtmlx.com/planning-engine/java/resources-and-assignment/skills-and-requirements.md>) - pools, skills, requirements and matches
