PowerSolver rule reference
Generated from powersolver-api/src/main/resources/rules/catalog.json — do not edit by hand. Run python -I powersolver-verify/render_rule_reference.py after changing the catalogue.
How every rule is evaluated
Every rule except unassigned_count looks only at fully assigned items: in basic mode every planning variable of the item has a value; in list mode the item is placed in some resource's list. Unassigned items are counted only by unassigned_count.
Value conventions
Lists: a JSON array, or text whose comma-separated parts (trimmed, empty parts dropped) are the entries. Comparison as text: both sides are turned into text first (JSON true/false become "true"/"false"). Times: an ISO local date-time such as 2026-06-01T09:00 (no zone), or a plain number; two times are compared only when both are date-times or both are numbers. Durations count in minutes.
Stay within capacity (capacity)
The total load given to a {variable_field} may not exceed its {capacity_property}.
Default severity: hard · Works with: basic variables
How a violation is counted: Per resource: total = round(sum of the assigned items' load) (load_property, or 1 when the param is omitted; added exactly, rounded once; a duration counts in hours); matches = sum over resources of max(0, total - capacity). Resources without capacity_property are unlimited. With resource_property instead of variable_field, the resources are the items whose ids the item names there (one or a list, any collection), and every fully assigned item adds its load to each of them. Such a rule reads only data: "Can this work?" reports it when the data breaks it.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | no | a planning variable id | any | — | The resource variable. Give this or resource_property, not both. |
resource_property | no | a property of the planned items | scalar_or_list | item not checked by this rule | Instead of variable_field: the resource fixed in the data, as the id of an item (or a list of ids) of any collection, e.g. the lesson's teacher. |
capacity_property | yes | a property of the values of variable_field | integer | item not checked by this rule | Each resource's capacity; absent means unlimited. |
load_property | no | a property of the planned items | number | rejected (every item needs it) | Each item's load (a number or a duration, in hours); when omitted every item counts 1. |
Minimise cost (cost)
Keep the total cost of the chosen {variable_field} as low as possible.
Default severity: soft · Works with: basic variables
How a violation is counted: Penalty = sum over assigned items of max(0, round(weight x rate x quantity / scale)); quantity = 1 and scale = 1 when omitted.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | yes | a planning variable id | any | — | The costed resource variable. |
rate_property | yes | a property of the values of variable_field | number | rejected (every item needs it) | Cost per unit of each resource. |
quantity_property | no | a property of the planned items | number | rejected (every item needs it) | Units per item; 1 when omitted. |
scale | no | a number | number | — | Divides every cost, to keep money comparable with other goals; must be > 0. Default: "1". |
Balance the load (load_balance)
Spread {entities} evenly across {variable}.
Default severity: soft · Works with: basic variables
How a violation is counted: For each value used, count c of items assigned to it; matches = sum of c squared. The lowest possible total for n items over k values is x*(q+1)^2 + (k-x)*q^2 with q = n div k, x = n mod k.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | yes | a planning variable id | any | — | The variable to balance. |
Days in a row (max_consecutive_days)
A person or resource works at most a given number of days in a row.
Default severity: hard · Works with: basic variables
How a violation is counted: Per resource (the assigned value of variable_field, or each id the item names in resource_property), the calendar days on which its fully assigned items start (slot_start_property of the chosen time option, or the item's entity_start_property; several items on one day count once; items without a date-time start are not counted). matches = sum over each run of consecutive days of max(0, run length - max_days); penalty = weight x matches.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | no | a planning variable id | any | — | The person or resource variable. Give this or resource_property, not both. |
resource_property | no | a property of the planned items | scalar_or_list | item not checked by this rule | Instead of variable_field: the person or resource fixed on each item (an id or a list of ids). |
slot_field | no | a planning variable id | any | — | The time variable, when the start comes from the chosen time option. |
slot_start_property | no | a property of the values of slot_field | datetime | item not checked by this rule | When each time option starts. |
entity_start_property | no | a property of the planned items | datetime | item not checked by this rule | Instead of slot_field: when each item starts. |
max_days | yes | a number | integer | — | The most days in a row (e.g. 5). |
Enough people per time slot (min_coverage)
Every {slot_field} gets at least the number of {entities} its {required_property} asks for.
Default severity: hard · Works with: basic variables
How a violation is counted: Per option of slot_field (every one, including options nobody is placed in): covered = the number of fully assigned items placed in it or, with entity_hours_property and slot_start_property, the items whose interval [start of their option, + hours) contains the option's start (a duration counts in hours, so PT4H = 4). Options without a numeric required_property ask for nothing. matches = sum over options of max(0, required - covered); penalty = weight x matches.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
slot_field | yes | a planning variable id | any | — | The time variable whose options carry the demand. |
required_property | yes | a property of the values of slot_field | integer | item not checked by this rule | How many items each time option needs; absent = none. |
slot_start_property | no | a property of the values of slot_field | datetime | item not checked by this rule | When each time option starts; needed with entity_hours_property. |
entity_hours_property | no | a property of the planned items | number | rejected (every item needs it) | Each item's length in hours: it covers every option starting during it. Omitted = an item covers only its own option. |
Minimum rest (min_rest)
Two {entities} of the same person or resource leave at least the minimum rest between them.
Default severity: hard · Works with: basic variables
How a violation is counted: Per resource (the assigned value of variable_field, or each id the item names in resource_property), one match per pair of its fully assigned items whose gap - from the end of the earlier to the start of the later, in either order; negative when they overlap - is shorter than min_rest_hours. Times come from the chosen time option (slot_field with slot_start_property and slot_end_property) or from the items (entity_start_property and entity_end_property); items without both times are not checked. Penalty per match = weight x missing rest in whole hours rounded up.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | no | a planning variable id | any | — | The person or resource variable. Give this or resource_property, not both. |
resource_property | no | a property of the planned items | scalar_or_list | item not checked by this rule | Instead of variable_field: the person or resource fixed on each item (an id or a list of ids). |
slot_field | no | a planning variable id | any | — | The time variable, when the times come from the chosen time option. |
slot_start_property | no | a property of the values of slot_field | datetime | item not checked by this rule | When each time option starts. |
slot_end_property | no | a property of the values of slot_field | datetime | item not checked by this rule | When each time option ends. |
entity_start_property | no | a property of the planned items | datetime | item not checked by this rule | Instead of slot_field: when each item starts. |
entity_end_property | no | a property of the planned items | datetime | item not checked by this rule | Instead of slot_field: when each item ends. |
min_rest_hours | yes | a number | number | — | Minimum rest between two items, in hours (e.g. 11). |
Keep a number low (minimize_property)
Keep the total {property} as low as possible.
Default severity: soft · Works with: basic, list variables
How a violation is counted: Penalty = weight x round(sum over assigned items of value) - the values are added exactly and only the total is rounded; matches = assigned items with a numeric value. A duration counts in hours (PT20M = 0.33). The value must be placement-dependent (a derived shadow) to influence the plan.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
property | yes | a derived shadow or a property of the planned items (shadow kinds: derived, index, property) | number | rejected (every item needs it) | A derived shadow (or numeric item property) to minimise. |
No double-booking (no_conflict)
No two {entities} may share the same {variables}; with {group_property}, only {entities} that share one of its values (e.g. the same class) are compared.
Default severity: hard · Works with: basic variables
How a violation is counted: Items are grouped by the combination of their values for all listed variables (items with any empty value are ignored); each group of k > 1 items gives k - 1 matches. With group_property, an item is counted once in each group it names (one value or a list; duplicates count once; items without it are ignored), so items clash only when they share a group and all listed values.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
fields | yes | a list of planning variable ids | any | — | The variables whose combined values must be unique, e.g. [person, time slot]. |
group_property | no | a property of the planned items | scalar_or_list | item not checked by this rule | Optional: who takes part in each item, fixed in the data (e.g. the class, or the list of classes, attending a lesson). With [time slot] it means: a class never has two lessons at the same time. |
Same topic at most once a day per group (no_subject_repeat_per_day)
A {group_property} may not have the same {category_property} twice on one {day_property}.
Default severity: hard · Works with: basic variables
How a violation is counted: Items are grouped by (group, category, day of the assigned slot); items missing any of the three are ignored; each group of k > 1 items gives k - 1 matches.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
group_property | yes | a property of the planned items | string | item not checked by this rule | Who attends (e.g. class). |
category_property | yes | a property of the planned items | string | item not checked by this rule | The topic (e.g. subject). |
slot_field | yes | a planning variable id | any | — | The time-slot variable. |
day_property | yes | a property of the values of slot_field | string | rejected (every item needs it) | The day of each slot. |
Limit per period (period_limit)
The total load of a person or resource in one period (a day, a week...) may not exceed its limit.
Default severity: hard · Works with: basic variables
How a violation is counted: Per resource (the assigned value of variable_field, or each id the item names in resource_property) and per period of period_days days (1 = calendar day, 7 = ISO week, Monday to Sunday), total = round(sum of the fully assigned items' load) (load_property, or 1 when omitted; added exactly, rounded once; a duration counts in hours, so PT12H = 12). An item belongs to the period its start falls in (slot_start_property of the chosen time option, or the item's entity_start_property); items without a date-time start are not counted. Limit = the resource's limit_property, else max_load; with neither the resource is unlimited. matches = sum over resources and periods of max(0, total - limit); penalty = weight x matches.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | no | a planning variable id | any | — | The person or resource variable. Give this or resource_property, not both. |
resource_property | no | a property of the planned items | scalar_or_list | item not checked by this rule | Instead of variable_field: the person or resource fixed on each item (an id or a list of ids). |
slot_field | no | a planning variable id | any | — | The time variable, when the start comes from the chosen time option. |
slot_start_property | no | a property of the values of slot_field | datetime | item not checked by this rule | When each time option starts. |
entity_start_property | no | a property of the planned items | datetime | item not checked by this rule | Instead of slot_field: when each item starts. |
period_days | yes | a number | integer | — | Length of the period in days: 1 = a calendar day, 7 = an ISO week (Monday to Sunday). |
limit_property | no | a property of the values of variable_field | number | item not checked by this rule | Each resource's own limit per period; when absent, max_load applies. |
max_load | no | a number | number | — | The limit per period for resources without limit_property; omitted = unlimited. |
load_property | no | a property of the planned items | number | rejected (every item needs it) | Each item's load (e.g. its hours); when omitted every item counts 1. |
Do prerequisites first (precedence)
An {entity} starts only after the {entities} it depends on.
Default severity: hard · Works with: basic variables
How a violation is counted: One match per ordered pair (a, b) where b's depends_on_property lists a's id, both are assigned, and a's slot time is not strictly before b's. An item that lists its own id is ignored (no self-dependency). Penalty per match = weight x (1 + whole days by which a starts at or after b; whole units for numeric times), so a slightly broken order costs less than a badly broken one.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
slot_field | yes | a planning variable id | any | — | The time-slot variable. |
value_property | yes | a property of the values of slot_field | datetime | rejected (every item needs it) | The time of each slot. |
depends_on_property | yes | a property of the planned items | id_list | item not checked by this rule | Ids of the items that must come first. |
Honour preferences (preference)
Give each {entity} a {variable_field} that prefers it.
Default severity: soft · Works with: basic variables
How a violation is counted: One match per item whose assigned value states a preference (non-empty value_property list) that does not contain the item's entity_property (comparison as text).
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | yes | a planning variable id | any | — | The variable holding the person/resource. |
entity_property | yes | a property of the planned items | string | item not checked by this rule | The item's attribute (e.g. shift type). |
value_property | yes | a property of the values of variable_field | scalar_or_list | item not checked by this rule | What each value prefers. |
Never use protected options (protected_slot)
No {entity} may use a {variable} marked {value_property}.
Default severity: hard · Works with: basic variables
How a violation is counted: One match per item whose assigned value has value_property = true (JSON boolean).
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | yes | a planning variable id | any | — | The variable to check. |
value_property | yes | a property of the values of variable_field | boolean | uses the default (false) | true marks a protected value; absent means not protected. |
Keep special resources for items that need them (resource_exclusivity)
A {variable} with a special capability only takes {entities} that need it.
Default severity: hard · Works with: basic variables
How a violation is counted: Reserved capabilities = capabilities some item requires (all items count, assigned or not). One match per item whose assigned value offers at least one reserved capability while the item requires none of the reserved capabilities that value offers.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | yes | a planning variable id | any | — | The variable holding the resource. |
entity_property | yes | a property of the planned items | scalar_or_list | item not checked by this rule | What each item needs. |
value_property | yes | a property of the values of variable_field | scalar_or_list | item not checked by this rule | What each resource offers. |
Respect unavailability (resource_unavailable)
A {variable_field} is never used when it is unavailable.
Default severity: hard · Works with: basic variables
How a violation is counted: One match per item whose assigned resource lists, in unavailable_property, the assigned slot's slot_property value (comparison as text). With resource_property instead of variable_field, the resources are the items whose ids the item names there (one or a list, any collection): one match per item when any of them is unavailable.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | no | a planning variable id | any | — | The resource variable. Give this or resource_property, not both. |
resource_property | no | a property of the planned items | scalar_or_list | item not checked by this rule | Instead of variable_field: the resource fixed in the data, as the id of an item (or a list of ids) of any collection, e.g. the lesson's teacher. |
slot_field | yes | a planning variable id | any | — | The time-slot variable. |
slot_property | yes | a property of the values of slot_field | string | rejected (every item needs it) | What is compared (e.g. day). |
unavailable_property | yes | a property of the values of variable_field | list | item not checked by this rule | The resource's unavailable list (on the variable's options, or on the items named by resource_property). |
Grouped items share a value (same_value_in_group)
{entities} with the same {group_property} must get the same {variable}.
Default severity: hard · Works with: basic variables
How a violation is counted: Assigned items are grouped by their group_property value (items without it are ignored); within a group, items are grouped again by their value of variable_field; a group whose items hold d > 1 different values gives d - 1 matches.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | yes | a planning variable id | any | — | The variable the grouped items must share, e.g. the time slot. |
group_property | yes | a property of the planned items | string | item not checked by this rule | The item property naming the group, e.g. crew. |
Capability must match (skill_match)
The {variable} given to each {entity} must have what the {entity} needs.
Default severity: hard · Works with: basic variables
How a violation is counted: One match per item whose assigned value does not have every required capability (required = list of the item's entity_property; capabilities = list of the value's value_property; comparison as text). Items with no requirement never match. With resource_property instead of variable_field, the resources are the items whose ids the item names there (one or a list, any collection): one match per item when any of them lacks a required capability. Such a rule reads only data: "Can this work?" reports it when the data breaks it.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | no | a planning variable id | any | — | The variable holding the person/machine/room. Give this or resource_property, not both. |
resource_property | no | a property of the planned items | scalar_or_list | item not checked by this rule | Instead of variable_field: the resource fixed in the data, as the id of an item (or a list of ids) of any collection, e.g. the lesson's teacher. |
entity_property | yes | a property of the planned items | scalar_or_list | item not checked by this rule | What each item needs. |
value_property | yes | a property of the values of variable_field | scalar_or_list | item not checked by this rule | What each value offers. |
Within the allowed time window (time_window)
Each {entity} must be scheduled between its {entity_start_property} and {entity_end_property}.
Default severity: hard · Works with: basic variables
How a violation is counted: One match per item whose assigned value's time is before the item's start bound or after its end bound. A missing bound is no bound.
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | yes | a planning variable id | any | — | The time-slot variable. |
value_property | yes | a property of the values of variable_field | datetime | rejected (every item needs it) | The time of each slot. |
entity_start_property | yes | a property of the planned items | datetime | item not checked by this rule | Earliest allowed time. |
entity_end_property | yes | a property of the planned items | datetime | item not checked by this rule | Latest allowed time. |
Everything gets a value (unassigned_count)
Every {entity} must get a {variable}.
Default severity: hard · Works with: basic, list variables
How a violation is counted: One match per item that has no value for the variable (basic: the variable is empty; list: the item is in no resource's list).
| Param | Required | Points to | Value type | If an item lacks it | Meaning |
|---|---|---|---|---|---|
variable_field | no | a planning variable id | any | — | The basic variable to check. Omit for a list variable. |
inverse_field | yes, unless variable_field is given | a derived shadow or a property of the planned items (shadow kinds: inverseRelation) | any | — | List mode only: the inverse-relation shadow naming the item's resource. Defaults to 'host'. |