Project.SuggestAssignments#

Proposes resources for the requirement slots of a Project. It fills empty slots, repairs broken ones and rebalances valid ones, and scores each step the way Evaluate does. The project isn't modified; the proposal comes back as a change set. Needs a solved project with no unsolved edits.

Signature#

public SuggestResult SuggestAssignments(IReadOnlyList<Slot> slots, SuggestOptions options)

Synchronous and CPU-only. Doesn't change the project's state.

Parameters#

Name Type Description
slots IReadOnlyList<Slot> The slots to fill or rebalance. To build them from skills and requirements, use SkillLayer.Slots. To build your own, read the current state with Roster.
options SuggestOptions Expected baseline, targets, fixed assignments, penalty, passes, leveling stage and ClearFirst

Behavior#

  • Greedy: it visits slots 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.
  • With options.ClearFirst, the proposal also carries the leveling clears it measured against. See SuggestOptions.
  • Commit the proposal with Apply(result.Changes), then Solve(null, …). When options.Level was set, call Level with the same leveling options instead.

Returns#

A SuggestResult with the proposed changes and a step log.

Errors#

Error When
ProjectException NoBaseline The project is unsolved, including after Apply
ProjectException StaleBaseline options.BaselineId doesn't name the current baseline
InputException Invalid options (newResourcePenalty, passes, targets[i], fixed[i], Level); a cyclic accepted state (Path is null); an invalid slot (paths under slots[i])
ArgumentNullException slots or options is null

It checks options before slots. The accepted values are on the SuggestOptions and Slot pages.

Example#

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

using Plen;

var start = Moment.FromYmdHm(2026, 8, 10, 0, 0) ?? throw new InvalidOperationException("invalid date");
var data = new ProjectData
{
    Tasks =
    [
        new TaskData("A") { StartDate = start, Duration = 2.0 },
        new TaskData("C") { StartDate = start, Duration = 2.0 },
    ],
    Resources = [new ResourceData { Id = "dev1" }, new ResourceData { Id = "dev2" }],
    Assignments = [new AssignmentData { Id = "aA", Task = "A", Resource = "dev1", Value = 1.0 }],
};
data.Config.UseCalendars = false;

var project = new Project();
project.Solve(data, new SolveOptions());

// Task C needs one person: dev1 or dev2.
List<Slot> slots = [new Slot("sC", "C", ["dev1", "dev2"])];
var result = project.SuggestAssignments(slots, new SuggestOptions());
foreach (var step in result.Steps)
    Console.WriteLine($"{step.Slot} -> {step.ToResource}"); // sC -> dev2

var report = project.Apply(result.Changes);
if (report.Rejected.Count == 0)
    project.Solve(null, new SolveOptions());