dabke 0.81.1 → 0.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +45 -27
- package/dist/client.d.ts +20 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -1
- package/dist/client.js.map +1 -1
- package/dist/client.types.d.ts +9 -0
- package/dist/client.types.d.ts.map +1 -1
- package/dist/client.types.js +1 -0
- package/dist/client.types.js.map +1 -1
- package/dist/cpsat/model-builder.d.ts +9 -0
- package/dist/cpsat/model-builder.d.ts.map +1 -1
- package/dist/cpsat/model-builder.js +36 -34
- package/dist/cpsat/model-builder.js.map +1 -1
- package/dist/cpsat/response.d.ts +13 -1
- package/dist/cpsat/response.d.ts.map +1 -1
- package/dist/cpsat/response.js +4 -0
- package/dist/cpsat/response.js.map +1 -1
- package/dist/cpsat/rules/cost-utils.d.ts +11 -0
- package/dist/cpsat/rules/cost-utils.d.ts.map +1 -0
- package/dist/cpsat/rules/cost-utils.js +24 -0
- package/dist/cpsat/rules/cost-utils.js.map +1 -0
- package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.js +3 -14
- package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.js +3 -7
- package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/index.d.ts +3 -0
- package/dist/cpsat/rules/index.d.ts.map +1 -1
- package/dist/cpsat/rules/index.js +3 -0
- package/dist/cpsat/rules/index.js.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.js +16 -2
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/max-days-week.d.ts +44 -0
- package/dist/cpsat/rules/max-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/max-days-week.js +95 -0
- package/dist/cpsat/rules/max-days-week.js.map +1 -0
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-day.js +15 -2
- package/dist/cpsat/rules/max-hours-day.js.map +1 -1
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-week.js +16 -2
- package/dist/cpsat/rules/max-hours-week.js.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.js +15 -2
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.js +15 -2
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/min-days-week.d.ts +34 -0
- package/dist/cpsat/rules/min-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/min-days-week.js +84 -0
- package/dist/cpsat/rules/min-days-week.js.map +1 -0
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-day.js +15 -2
- package/dist/cpsat/rules/min-hours-day.js.map +1 -1
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-week.js +16 -2
- package/dist/cpsat/rules/min-hours-week.js.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.js +72 -2
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
- package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -1
- package/dist/cpsat/rules/minimize-cost.js +2 -23
- package/dist/cpsat/rules/minimize-cost.js.map +1 -1
- package/dist/cpsat/rules/must-assign.d.ts +49 -0
- package/dist/cpsat/rules/must-assign.d.ts.map +1 -0
- package/dist/cpsat/rules/must-assign.js +86 -0
- package/dist/cpsat/rules/must-assign.js.map +1 -0
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-multiplier.js +1 -12
- package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.js +1 -5
- package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +5 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.js +1 -12
- package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.js +1 -12
- package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.js +1 -5
- package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -1
- package/dist/cpsat/rules/registry.d.ts +28 -2
- package/dist/cpsat/rules/registry.d.ts.map +1 -1
- package/dist/cpsat/rules/registry.js +4 -1
- package/dist/cpsat/rules/registry.js.map +1 -1
- package/dist/cpsat/rules/resolver.js +2 -2
- package/dist/cpsat/rules/resolver.js.map +1 -1
- package/dist/cpsat/rules/rules.types.d.ts +3 -0
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.d.ts +18 -1
- package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.js +59 -16
- package/dist/cpsat/rules/scope.types.js.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.js +2 -1
- package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/time-off.d.ts.map +1 -1
- package/dist/cpsat/rules/time-off.js +6 -3
- package/dist/cpsat/rules/time-off.js.map +1 -1
- package/dist/cpsat/semantic-time.d.ts +44 -42
- package/dist/cpsat/semantic-time.d.ts.map +1 -1
- package/dist/cpsat/semantic-time.js +64 -46
- package/dist/cpsat/semantic-time.js.map +1 -1
- package/dist/cpsat/types.d.ts +37 -27
- package/dist/cpsat/types.d.ts.map +1 -1
- package/dist/cpsat/utils.d.ts.map +1 -1
- package/dist/cpsat/utils.js +7 -12
- package/dist/cpsat/utils.js.map +1 -1
- package/dist/cpsat/validation-reporter.d.ts +10 -7
- package/dist/cpsat/validation-reporter.d.ts.map +1 -1
- package/dist/cpsat/validation-reporter.js +44 -72
- package/dist/cpsat/validation-reporter.js.map +1 -1
- package/dist/cpsat/validation.types.d.ts +54 -44
- package/dist/cpsat/validation.types.d.ts.map +1 -1
- package/dist/cpsat/validation.types.js +15 -10
- package/dist/cpsat/validation.types.js.map +1 -1
- package/dist/datetime.utils.d.ts +3 -203
- package/dist/datetime.utils.d.ts.map +1 -1
- package/dist/datetime.utils.js +1 -288
- package/dist/datetime.utils.js.map +1 -1
- package/dist/index.d.ts +14 -83
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -83
- package/dist/index.js.map +1 -1
- package/dist/schedule/cost.d.ts +204 -0
- package/dist/schedule/cost.d.ts.map +1 -0
- package/dist/schedule/cost.js +187 -0
- package/dist/schedule/cost.js.map +1 -0
- package/dist/schedule/coverage.d.ts +85 -0
- package/dist/schedule/coverage.d.ts.map +1 -0
- package/dist/schedule/coverage.js +33 -0
- package/dist/schedule/coverage.js.map +1 -0
- package/dist/schedule/definition.d.ts +227 -0
- package/dist/schedule/definition.d.ts.map +1 -0
- package/dist/schedule/definition.js +659 -0
- package/dist/schedule/definition.js.map +1 -0
- package/dist/schedule/index.d.ts +67 -0
- package/dist/schedule/index.d.ts.map +1 -0
- package/dist/schedule/index.js +69 -0
- package/dist/schedule/index.js.map +1 -0
- package/dist/schedule/rules.d.ts +353 -0
- package/dist/schedule/rules.d.ts.map +1 -0
- package/dist/schedule/rules.js +352 -0
- package/dist/schedule/rules.js.map +1 -0
- package/dist/schedule/shift-patterns.d.ts +34 -0
- package/dist/schedule/shift-patterns.d.ts.map +1 -0
- package/dist/schedule/shift-patterns.js +41 -0
- package/dist/schedule/shift-patterns.js.map +1 -0
- package/dist/schedule/time-periods.d.ts +69 -0
- package/dist/schedule/time-periods.d.ts.map +1 -0
- package/dist/schedule/time-periods.js +91 -0
- package/dist/schedule/time-periods.js.map +1 -0
- package/dist/types.d.ts +14 -78
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -9
- package/solver/src/solver/app.py +1 -1
- package/solver/src/solver/solver.py +7 -4
- package/src/client.ts +6 -8
- package/src/client.types.ts +9 -0
- package/src/cpsat/model-builder.ts +44 -35
- package/src/cpsat/response.ts +13 -1
- package/src/cpsat/rules/cost-utils.ts +25 -0
- package/src/cpsat/rules/day-cost-multiplier.ts +3 -14
- package/src/cpsat/rules/day-cost-surcharge.ts +3 -8
- package/src/cpsat/rules/index.ts +3 -0
- package/src/cpsat/rules/max-consecutive-days.ts +17 -0
- package/src/cpsat/rules/max-days-week.ts +143 -0
- package/src/cpsat/rules/max-hours-day.ts +21 -1
- package/src/cpsat/rules/max-hours-week.ts +22 -1
- package/src/cpsat/rules/max-shifts-day.ts +21 -1
- package/src/cpsat/rules/min-consecutive-days.ts +16 -1
- package/src/cpsat/rules/min-days-week.ts +120 -0
- package/src/cpsat/rules/min-hours-day.ts +16 -1
- package/src/cpsat/rules/min-hours-week.ts +17 -1
- package/src/cpsat/rules/min-rest-between-shifts.ts +92 -2
- package/src/cpsat/rules/minimize-cost.ts +2 -29
- package/src/cpsat/rules/must-assign.ts +108 -0
- package/src/cpsat/rules/overtime-daily-multiplier.ts +1 -12
- package/src/cpsat/rules/overtime-daily-surcharge.ts +1 -6
- package/src/cpsat/rules/overtime-tiered-multiplier.ts +6 -13
- package/src/cpsat/rules/overtime-weekly-multiplier.ts +1 -12
- package/src/cpsat/rules/overtime-weekly-surcharge.ts +1 -6
- package/src/cpsat/rules/registry.ts +8 -2
- package/src/cpsat/rules/resolver.ts +2 -2
- package/src/cpsat/rules/rules.types.ts +3 -0
- package/src/cpsat/rules/scope.types.ts +73 -20
- package/src/cpsat/rules/time-cost-surcharge.ts +2 -1
- package/src/cpsat/rules/time-off.ts +6 -2
- package/src/cpsat/semantic-time.ts +115 -91
- package/src/cpsat/types.ts +37 -27
- package/src/cpsat/utils.ts +8 -12
- package/src/cpsat/validation-reporter.ts +51 -82
- package/src/cpsat/validation.types.ts +72 -47
- package/src/datetime.utils.ts +3 -334
- package/src/index.ts +35 -107
- package/src/schedule/cost.ts +242 -0
- package/src/schedule/coverage.ts +135 -0
- package/src/schedule/definition.ts +958 -0
- package/src/schedule/index.ts +112 -0
- package/src/schedule/rules.ts +529 -0
- package/src/schedule/shift-patterns.ts +46 -0
- package/src/schedule/time-periods.ts +110 -0
- package/src/types.ts +14 -88
- package/dist/errors.d.ts +0 -12
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -17
- package/dist/errors.js.map +0 -1
- package/dist/llms.d.ts +0 -2
- package/dist/llms.d.ts.map +0 -1
- package/dist/llms.js +0 -3
- package/dist/llms.js.map +0 -1
- package/dist/schedule.d.ts +0 -724
- package/dist/schedule.d.ts.map +0 -1
- package/dist/schedule.js +0 -899
- package/dist/schedule.js.map +0 -1
- package/dist/validation.d.ts +0 -105
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -130
- package/dist/validation.js.map +0 -1
- package/llms.txt +0 -925
- package/src/errors.ts +0 -17
- package/src/llms.ts +0 -3
- package/src/schedule.ts +0 -1419
- package/src/validation.ts +0 -188
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,64 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
This changelog was generated from the git history of the project when it was
|
|
6
6
|
named `scheduling-core`, prior to the rename to `dabke` in v0.78.0.
|
|
7
7
|
|
|
8
|
+
## 0.83.0 (2026-03-21)
|
|
9
|
+
|
|
10
|
+
### Breaking Changes
|
|
11
|
+
|
|
12
|
+
- Remove `./llms` export path and `llms.txt` from the package. The LLM reference
|
|
13
|
+
is now generated externally via `generate:reference`.
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
- Add `maxDaysPerWeek(days, opts?)` and `minDaysPerWeek(days, opts?)` rule functions
|
|
18
|
+
to constrain the number of distinct working days per member per scheduling week.
|
|
19
|
+
- Add `mustAssign(opts?)` rule for staffing obligations. Guarantees targeted members
|
|
20
|
+
appear on the schedule each week; defaults to HIGH priority so the schedule still
|
|
21
|
+
generates when a member cannot be placed.
|
|
22
|
+
|
|
23
|
+
### Improvements
|
|
24
|
+
|
|
25
|
+
- Split `schedule.ts` into focused modules under `src/schedule/` (public API unchanged).
|
|
26
|
+
- Add `NoInfer` on coverage entry types so role/skill references are checked against
|
|
27
|
+
declared roles and skills at compile time.
|
|
28
|
+
- Update devDependencies (@types/node ^25.0.0, commander ^14.0.3).
|
|
29
|
+
- Publish workflow now includes changelog text in GitHub releases.
|
|
30
|
+
|
|
31
|
+
## 0.82.0 (2026-02-23)
|
|
32
|
+
|
|
33
|
+
### Breaking Changes
|
|
34
|
+
|
|
35
|
+
- **Replace `defineSchedule` with `schedule`/`with`/`compile`/`solve` API**: the
|
|
36
|
+
`defineSchedule()` function, `ScheduleDefinition`, and `RuntimeArgs` are removed.
|
|
37
|
+
Use `schedule()` to create an immutable `Schedule`, compose data with `.with()`,
|
|
38
|
+
and execute via `.compile()` or `.solve()`.
|
|
39
|
+
- **Rename `roles`/`skills` back to `roleIds`/`skillIds`**: all public type fields and
|
|
40
|
+
function options (`SchedulingMember`, `ShiftPattern`, coverage types, `ScheduleConfig`)
|
|
41
|
+
revert to `roleIds`/`skillIds` for unambiguous signaling to LLM consumers.
|
|
42
|
+
- **Redesign validation message architecture**: `ValidationGroup` now uses deterministic
|
|
43
|
+
structural keys (e.g. `rule:max-hours-week:40:roles:nurse`) instead of counter-based
|
|
44
|
+
IDs. Validation items use a unified `message` field replacing `reason`/`description`.
|
|
45
|
+
`ValidationSummary.title` replaces `description`.
|
|
46
|
+
- **Trim public API surface** (102 to 86 exports): remove internal types
|
|
47
|
+
(`ValidationReporter`, `ValidationContext`, `CostContext`, `ModelBuilderOptions`,
|
|
48
|
+
`CoverageRequirement`, `CpsatRuleRegistry`, `DateTime`), legacy validation, and
|
|
49
|
+
unused datetime utilities.
|
|
50
|
+
- **`SolveResult` status** uses lowercase strings: `optimal`, `feasible`, `infeasible`,
|
|
51
|
+
`no_solution`.
|
|
52
|
+
|
|
53
|
+
### Improvements
|
|
54
|
+
|
|
55
|
+
- `solve()` runs post-solve validation automatically; `SolveResult.validation` includes
|
|
56
|
+
soft constraint violations.
|
|
57
|
+
- `ScheduleConfig` requires `times`, `coverage`, `shiftPatterns`; `partialSchedule()`
|
|
58
|
+
allows partial configs for composition via `.with()`.
|
|
59
|
+
- Eager validation in `.with()` for role/skill disjointness and member references.
|
|
60
|
+
- All `dayOfWeek` fields typed as `readonly [DayOfWeek, ...DayOfWeek[]]` consistently.
|
|
61
|
+
- Add constraint IDs, group keys, and `trackConstraint()` to all soft rules for
|
|
62
|
+
post-solve verification.
|
|
63
|
+
- Add `@category` TSDoc tags to all public exports for structured documentation.
|
|
64
|
+
- Restructure docs reference nav with automated category-driven generation.
|
|
65
|
+
|
|
8
66
|
## 0.81.1 (2026-02-20)
|
|
9
67
|
|
|
10
68
|
### Documentation
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Define teams, shifts, coverage, and rules declaratively. dabke compiles them int
|
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
12
|
import {
|
|
13
|
-
|
|
13
|
+
schedule,
|
|
14
14
|
t,
|
|
15
15
|
time,
|
|
16
16
|
cover,
|
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
weekend,
|
|
24
24
|
} from "dabke";
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
|
|
26
|
+
const venue = schedule({
|
|
27
|
+
roleIds: ["barista", "server"],
|
|
28
28
|
|
|
29
29
|
// Times: WHEN you need people (named periods for coverage + rules)
|
|
30
30
|
times: {
|
|
@@ -75,31 +75,39 @@ docker pull christianklotz/dabke-solver
|
|
|
75
75
|
docker run -p 8080:8080 christianklotz/dabke-solver
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
Once you have a schedule definition,
|
|
78
|
+
Once you have a schedule definition, add members and solve:
|
|
79
79
|
|
|
80
80
|
```typescript
|
|
81
|
-
import {
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
81
|
+
import { HttpSolverClient } from "dabke";
|
|
82
|
+
|
|
83
|
+
const result = await venue
|
|
84
|
+
.with([
|
|
85
|
+
{ id: "alice", roleIds: ["barista", "server"], pay: { hourlyRate: 1500 } },
|
|
86
|
+
{ id: "bob", roleIds: ["barista"], pay: { hourlyRate: 1200 } },
|
|
87
|
+
{ id: "carol", roleIds: ["barista", "server"], pay: { hourlyRate: 1400 } },
|
|
88
|
+
{ id: "dave", roleIds: ["barista", "server"], pay: { hourlyRate: 1200 } },
|
|
89
|
+
{ id: "eve", roleIds: ["barista", "server"], pay: { hourlyRate: 1300 } },
|
|
90
|
+
])
|
|
91
|
+
.solve(new HttpSolverClient(fetch, "http://localhost:8080"), {
|
|
85
92
|
dateRange: { start: "2026-02-09", end: "2026-02-15" },
|
|
86
|
-
}
|
|
87
|
-
members: [
|
|
88
|
-
{ id: "alice", roles: ["barista", "server"], pay: { hourlyRate: 1500 } },
|
|
89
|
-
{ id: "bob", roles: ["barista"], pay: { hourlyRate: 1200 } },
|
|
90
|
-
{ id: "carol", roles: ["barista", "server"], pay: { hourlyRate: 1400 } },
|
|
91
|
-
{ id: "dave", roles: ["barista", "server"], pay: { hourlyRate: 1200 } },
|
|
92
|
-
{ id: "eve", roles: ["barista", "server"], pay: { hourlyRate: 1300 } },
|
|
93
|
-
],
|
|
94
|
-
});
|
|
93
|
+
});
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
console.log(result.status); // "optimal" | "feasible" | "infeasible" | "no_solution"
|
|
96
|
+
console.log(result.assignments); // Shift assignments per member per day
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Or compile and solve manually for more control:
|
|
98
100
|
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
```typescript
|
|
102
|
+
const compiled = venue
|
|
103
|
+
.with([...members])
|
|
104
|
+
.compile({ dateRange: { start: "2026-02-09", end: "2026-02-15" } });
|
|
105
|
+
|
|
106
|
+
if (compiled.canSolve) {
|
|
107
|
+
const solver = new HttpSolverClient(fetch, "http://localhost:8080");
|
|
108
|
+
const response = await solver.solve(compiled.request);
|
|
109
|
+
// ...
|
|
110
|
+
}
|
|
103
111
|
```
|
|
104
112
|
|
|
105
113
|
---
|
|
@@ -122,7 +130,14 @@ maxHoursPerWeek(30, { appliesTo: "bob", priority: "MEDIUM" }),
|
|
|
122
130
|
|
|
123
131
|
**Cost optimization.** Members declare `pay` as hourly or salaried. Cost rules (`minimizeCost`, `dayMultiplier`, `overtimeMultiplier`, `tieredOvertimeMultiplier`, etc.) tell the solver to minimize total labor cost, factoring in overtime, day premiums, and time-based surcharges.
|
|
124
132
|
|
|
125
|
-
**
|
|
133
|
+
**Composition with `.with()`.** Schedule definitions are immutable. Use `.with()` to merge other schedules or add members at runtime. Each `.with()` returns a new `Schedule` instance:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const ready = venue.with([
|
|
137
|
+
{ id: "alice", roleIds: ["barista"], pay: { hourlyRate: 1500 } },
|
|
138
|
+
{ id: "bob", roleIds: ["barista"], pay: { hourlyRate: 1200 } },
|
|
139
|
+
]);
|
|
140
|
+
```
|
|
126
141
|
|
|
127
142
|
---
|
|
128
143
|
|
|
@@ -163,12 +178,15 @@ maxHoursPerWeek(30, { appliesTo: "bob", priority: "MEDIUM" }),
|
|
|
163
178
|
|
|
164
179
|
## LLM Integration
|
|
165
180
|
|
|
166
|
-
|
|
181
|
+
Generate per-section API reference docs from TSDoc:
|
|
167
182
|
|
|
168
|
-
```
|
|
169
|
-
|
|
183
|
+
```bash
|
|
184
|
+
npm run generate:reference -- --outdir ./references
|
|
170
185
|
```
|
|
171
186
|
|
|
187
|
+
This produces a `README.md` index, an `api.md` API surface listing,
|
|
188
|
+
and one detail file per category (schedule, rules, coverage, etc.).
|
|
189
|
+
|
|
172
190
|
---
|
|
173
191
|
|
|
174
192
|
## Development
|
package/dist/client.d.ts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
|
-
import { type FetcherLike, type SolverClient, type SolverRequest
|
|
1
|
+
import { type FetcherLike, type SolverClient, type SolverRequest } from "./client.types.js";
|
|
2
2
|
export type { SolverClient, SolverRequest, SolverResponse, SolverVariable, SolverConstraint, SolverTerm, SolverObjective, FetcherLike, } from "./client.types.js";
|
|
3
3
|
/**
|
|
4
4
|
* Generic HTTP client for the solver service.
|
|
5
|
+
*
|
|
6
|
+
* @category Solver
|
|
5
7
|
*/
|
|
6
8
|
export declare class HttpSolverClient implements SolverClient {
|
|
7
9
|
#private;
|
|
8
10
|
constructor(fetcher: FetcherLike, baseUrl?: string);
|
|
9
11
|
solve(request: SolverRequest, options?: {
|
|
10
12
|
signal?: AbortSignal;
|
|
11
|
-
}): Promise<
|
|
13
|
+
}): Promise<{
|
|
14
|
+
status: "OPTIMAL" | "FEASIBLE" | "INFEASIBLE" | "TIMEOUT" | "ERROR";
|
|
15
|
+
values?: Record<string, number> | undefined;
|
|
16
|
+
statistics?: {
|
|
17
|
+
solveTimeMs?: number | undefined;
|
|
18
|
+
conflicts?: number | undefined;
|
|
19
|
+
branches?: number | undefined;
|
|
20
|
+
} | undefined;
|
|
21
|
+
error?: string | undefined;
|
|
22
|
+
solutionInfo?: string | undefined;
|
|
23
|
+
softViolations?: {
|
|
24
|
+
constraintId: string;
|
|
25
|
+
violationAmount: number;
|
|
26
|
+
targetValue: number;
|
|
27
|
+
actualValue: number;
|
|
28
|
+
}[] | undefined;
|
|
29
|
+
}>;
|
|
12
30
|
health(): Promise<void>;
|
|
13
31
|
}
|
|
14
32
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAG5F,YAAY,EACV,YAAY,EACZ,aAAa,EACb,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAS3B;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,YAAY;;gBAIvC,OAAO,EAAE,WAAW,EAAE,OAAO,GAAE,MAAgC;IAKrE,KAAK,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE;;;;;;;;;;;;;;;;;IAqBhE,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ9B"}
|
package/dist/client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SolverResponseSchema } from "./client.schemas.js";
|
|
1
2
|
const normalizeFetch = (fetcher) => {
|
|
2
3
|
if (typeof fetcher === "function")
|
|
3
4
|
return fetcher;
|
|
@@ -5,6 +6,8 @@ const normalizeFetch = (fetcher) => {
|
|
|
5
6
|
};
|
|
6
7
|
/**
|
|
7
8
|
* Generic HTTP client for the solver service.
|
|
9
|
+
*
|
|
10
|
+
* @category Solver
|
|
8
11
|
*/
|
|
9
12
|
export class HttpSolverClient {
|
|
10
13
|
#fetch;
|
|
@@ -28,7 +31,7 @@ export class HttpSolverClient {
|
|
|
28
31
|
if (!bodyText) {
|
|
29
32
|
throw new Error("Solver returned an empty response");
|
|
30
33
|
}
|
|
31
|
-
return JSON.parse(bodyText);
|
|
34
|
+
return SolverResponseSchema.parse(JSON.parse(bodyText));
|
|
32
35
|
}
|
|
33
36
|
async health() {
|
|
34
37
|
const res = await this.#fetch(`${this.#baseUrl}/health`);
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAa3D,MAAM,cAAc,GAAG,CACrB,OAAoB,EAC8C,EAAE;IACpE,IAAI,OAAO,OAAO,KAAK,UAAU;QAAE,OAAO,OAAO,CAAC;IAClD,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,OAAO,gBAAgB;IAC3B,MAAM,CAAiE;IACvE,QAAQ,CAAS;IAEjB,YAAY,OAAoB,EAAE,UAAkB,uBAAuB;QACzE,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAsB,EAAE,OAAkC;QACpE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,QAAQ,EAAE;YACtD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;YAC7B,MAAM,EAAE,OAAO,EAAE,MAAM;SACxB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAClC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,SAAS,CAAC,CAAC;QACzD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAChD,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;CACF"}
|
package/dist/client.types.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export type SolverResponse = z.infer<typeof SolverResponseSchema>;
|
|
|
64
64
|
* Solver outcome status.
|
|
65
65
|
*
|
|
66
66
|
* One of `"OPTIMAL"`, `"FEASIBLE"`, `"INFEASIBLE"`, `"TIMEOUT"`, or `"ERROR"`.
|
|
67
|
+
*
|
|
68
|
+
* @category Solver
|
|
67
69
|
*/
|
|
68
70
|
export type SolverStatus = z.infer<typeof SolverStatusSchema>;
|
|
69
71
|
/**
|
|
@@ -73,6 +75,7 @@ export type SolverStatus = z.infer<typeof SolverStatusSchema>;
|
|
|
73
75
|
* - `violationAmount` (required): magnitude of the violation
|
|
74
76
|
*/
|
|
75
77
|
export type SoftConstraintViolation = z.infer<typeof SoftConstraintViolationSchema>;
|
|
78
|
+
/** Convenience constants for {@link SolverStatus} values. */
|
|
76
79
|
export declare const SOLVER_STATUS: {
|
|
77
80
|
readonly OPTIMAL: "OPTIMAL";
|
|
78
81
|
readonly FEASIBLE: "FEASIBLE";
|
|
@@ -80,9 +83,15 @@ export declare const SOLVER_STATUS: {
|
|
|
80
83
|
readonly TIMEOUT: "TIMEOUT";
|
|
81
84
|
readonly ERROR: "ERROR";
|
|
82
85
|
};
|
|
86
|
+
/** A `fetch` function or an object with a `fetch` method. */
|
|
83
87
|
export type FetcherLike = typeof fetch | {
|
|
84
88
|
fetch: typeof fetch;
|
|
85
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Interface for sending solver requests and receiving responses.
|
|
92
|
+
*
|
|
93
|
+
* @category Solver
|
|
94
|
+
*/
|
|
86
95
|
export interface SolverClient {
|
|
87
96
|
solve(request: SolverRequest, options?: {
|
|
88
97
|
signal?: AbortSignal;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.types.d.ts","sourceRoot":"","sources":["../src/client.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,6BAA6B,EAC9B,MAAM,qBAAqB,CAAC;AAM7B;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE
|
|
1
|
+
{"version":3,"file":"client.types.d.ts","sourceRoot":"","sources":["../src/client.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,6BAA6B,EAC9B,MAAM,qBAAqB,CAAC;AAM7B;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAMpF,6DAA6D;AAC7D,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAC;AAMX,6DAA6D;AAC7D,MAAM,MAAM,WAAW,GACnB,OAAO,KAAK,GACZ;IACE,KAAK,EAAE,OAAO,KAAK,CAAC;CACrB,CAAC;AAEN;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC3F,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
|
package/dist/client.types.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// --------------------------------------------------------------------------
|
|
9
9
|
// Status constants (for convenience)
|
|
10
10
|
// --------------------------------------------------------------------------
|
|
11
|
+
/** Convenience constants for {@link SolverStatus} values. */
|
|
11
12
|
export const SOLVER_STATUS = {
|
|
12
13
|
OPTIMAL: "OPTIMAL",
|
|
13
14
|
FEASIBLE: "FEASIBLE",
|
package/dist/client.types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.types.js","sourceRoot":"","sources":["../src/client.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"client.types.js","sourceRoot":"","sources":["../src/client.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8FH,6EAA6E;AAC7E,qCAAqC;AACrC,6EAA6E;AAE7E,6DAA6D;AAC7D,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;IACpB,UAAU,EAAE,YAAY;IACxB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;CACN,CAAC"}
|
|
@@ -62,6 +62,11 @@ export interface CostEntry {
|
|
|
62
62
|
export interface CostContribution {
|
|
63
63
|
entries: CostEntry[];
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Result of {@link ModelBuilder.compile}.
|
|
67
|
+
*
|
|
68
|
+
* @category Model Builder
|
|
69
|
+
*/
|
|
65
70
|
export interface CompilationResult {
|
|
66
71
|
request: SolverRequest;
|
|
67
72
|
validation: ScheduleValidation;
|
|
@@ -70,6 +75,8 @@ export interface CompilationResult {
|
|
|
70
75
|
/**
|
|
71
76
|
* Configuration for ModelBuilder.
|
|
72
77
|
*
|
|
78
|
+
* @category Model Builder
|
|
79
|
+
*
|
|
73
80
|
* @example Date range with day-of-week filtering (restaurant closed Mon/Tue)
|
|
74
81
|
* ```typescript
|
|
75
82
|
* const config: ModelBuilderConfig = {
|
|
@@ -114,6 +121,8 @@ export interface ModelBuilderConfig extends ModelBuilderOptions {
|
|
|
114
121
|
/**
|
|
115
122
|
* Compilation context that creates variables, constraints, and objectives
|
|
116
123
|
* and emits a `SolverRequest` for the Python CP-SAT solver service.
|
|
124
|
+
*
|
|
125
|
+
* @category Model Builder
|
|
117
126
|
*/
|
|
118
127
|
export declare class ModelBuilder {
|
|
119
128
|
#private;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-builder.d.ts","sourceRoot":"","sources":["../../src/cpsat/model-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,aAAa,EAAkB,MAAM,oBAAoB,CAAC;AAC1F,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAU/D,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,IAAI,EACL,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAIvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAqB,MAAM,uBAAuB,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE9E;;;GAGG;AACH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,YAAY,EAAE,CAAC;CACxC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,0DAA0D;IAC1D,QAAQ,CAAC,CACP,WAAW,EAAE,uBAAuB,EAAE,EACtC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,qBAAqB,GAC7B,IAAI,CAAC;IACR,uDAAuD;IACvD,IAAI,CAAC,CACH,WAAW,EAAE,eAAe,EAAE,EAC9B,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,EACxC,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,GACzC,gBAAgB,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED
|
|
1
|
+
{"version":3,"file":"model-builder.d.ts","sourceRoot":"","sources":["../../src/cpsat/model-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,8 @@ import { ValidationReporterImpl } from "./validation-reporter.js";
|
|
|
6
6
|
/**
|
|
7
7
|
* Compilation context that creates variables, constraints, and objectives
|
|
8
8
|
* and emits a `SolverRequest` for the Python CP-SAT solver service.
|
|
9
|
+
*
|
|
10
|
+
* @category Model Builder
|
|
9
11
|
*/
|
|
10
12
|
export class ModelBuilder {
|
|
11
13
|
members;
|
|
@@ -41,8 +43,8 @@ export class ModelBuilder {
|
|
|
41
43
|
}
|
|
42
44
|
// Validate coverage requirements have at least roles or skills
|
|
43
45
|
for (const cov of config.coverage) {
|
|
44
|
-
const hasRoles = cov.
|
|
45
|
-
const hasSkills = cov.
|
|
46
|
+
const hasRoles = cov.roleIds !== undefined && cov.roleIds.length > 0;
|
|
47
|
+
const hasSkills = cov.skillIds !== undefined && cov.skillIds.length > 0;
|
|
46
48
|
if (!hasRoles && !hasSkills) {
|
|
47
49
|
throw new Error(`Coverage requirement for day "${cov.day}" must have at least one of roles or skills`);
|
|
48
50
|
}
|
|
@@ -158,7 +160,7 @@ export class ModelBuilder {
|
|
|
158
160
|
this.#objective.push({ var: varName, coeff: rounded });
|
|
159
161
|
}
|
|
160
162
|
membersWithRole(roleId) {
|
|
161
|
-
return this.members.filter((m) => m.
|
|
163
|
+
return this.members.filter((m) => m.roleIds.includes(roleId));
|
|
162
164
|
}
|
|
163
165
|
/**
|
|
164
166
|
* Returns team members who can satisfy a coverage requirement.
|
|
@@ -170,15 +172,15 @@ export class ModelBuilder {
|
|
|
170
172
|
*/
|
|
171
173
|
membersForCoverage(cov) {
|
|
172
174
|
return this.members.filter((m) => {
|
|
173
|
-
if (cov.
|
|
174
|
-
const hasMatchingRole = cov.
|
|
175
|
+
if (cov.roleIds && cov.roleIds.length > 0) {
|
|
176
|
+
const hasMatchingRole = cov.roleIds.some((role) => m.roleIds.includes(role));
|
|
175
177
|
if (!hasMatchingRole) {
|
|
176
178
|
return false;
|
|
177
179
|
}
|
|
178
180
|
}
|
|
179
|
-
if (cov.
|
|
180
|
-
const memberSkills = m.
|
|
181
|
-
if (!cov.
|
|
181
|
+
if (cov.skillIds && cov.skillIds.length > 0) {
|
|
182
|
+
const memberSkills = m.skillIds ?? [];
|
|
183
|
+
if (!cov.skillIds.every((skill) => memberSkills.includes(skill))) {
|
|
182
184
|
return false;
|
|
183
185
|
}
|
|
184
186
|
}
|
|
@@ -186,10 +188,10 @@ export class ModelBuilder {
|
|
|
186
188
|
});
|
|
187
189
|
}
|
|
188
190
|
canAssign(member, pattern) {
|
|
189
|
-
if (!pattern.
|
|
191
|
+
if (!pattern.roleIds || pattern.roleIds.length === 0) {
|
|
190
192
|
return true;
|
|
191
193
|
}
|
|
192
|
-
return pattern.
|
|
194
|
+
return pattern.roleIds.some((role) => member.roleIds.includes(role));
|
|
193
195
|
}
|
|
194
196
|
/**
|
|
195
197
|
* Checks if a shift pattern can be used on a specific day.
|
|
@@ -304,12 +306,12 @@ export class ModelBuilder {
|
|
|
304
306
|
for (const cov of this.coverage) {
|
|
305
307
|
const covStart = timeOfDayToMinutes(cov.startTime);
|
|
306
308
|
const covEnd = normalizeEndMinutes(covStart, timeOfDayToMinutes(cov.endTime));
|
|
307
|
-
const covKey = cov.
|
|
308
|
-
const coverageLabel = cov.
|
|
309
|
-
? cov.
|
|
310
|
-
? `role "${cov.
|
|
311
|
-
: `role "${cov.
|
|
312
|
-
: `skills [${cov.
|
|
309
|
+
const covKey = cov.roleIds?.join(",") ?? cov.skillIds?.join(",") ?? "unknown";
|
|
310
|
+
const coverageLabel = cov.roleIds && cov.roleIds.length > 0
|
|
311
|
+
? cov.roleIds.length === 1
|
|
312
|
+
? `role "${cov.roleIds[0]}"`
|
|
313
|
+
: `role "${cov.roleIds.join(" or ")}"`
|
|
314
|
+
: `skills [${cov.skillIds?.join(", ")}]`;
|
|
313
315
|
const coverageWindow = formatTimeRange(covStart, covEnd);
|
|
314
316
|
const eligibleMembers = this.membersForCoverage(cov);
|
|
315
317
|
if (eligibleMembers.length === 0) {
|
|
@@ -317,16 +319,16 @@ export class ModelBuilder {
|
|
|
317
319
|
this.reporter.reportCoverageError({
|
|
318
320
|
day: cov.day,
|
|
319
321
|
timeSlots: [coverageWindow],
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
322
|
+
roleIds: cov.roleIds,
|
|
323
|
+
skillIds: cov.skillIds,
|
|
324
|
+
message: `Coverage for ${coverageLabel} on ${cov.day} (${coverageWindow}) cannot be met: no eligible team members available.`,
|
|
323
325
|
suggestions: [
|
|
324
|
-
cov.
|
|
325
|
-
? `Add team members with role "${cov.
|
|
326
|
+
cov.roleIds && cov.roleIds.length > 0
|
|
327
|
+
? `Add team members with role "${cov.roleIds.join(" or ")}"`
|
|
326
328
|
: "Add team members with the required skills",
|
|
327
329
|
"Change the coverage requirement to match available team members",
|
|
328
330
|
],
|
|
329
|
-
|
|
331
|
+
group: cov.group,
|
|
330
332
|
});
|
|
331
333
|
const impossibleVar = `infeasible:coverage:${covKey}:${cov.day}`;
|
|
332
334
|
this.intVar(impossibleVar, 0, 0);
|
|
@@ -348,7 +350,7 @@ export class ModelBuilder {
|
|
|
348
350
|
recordBucketIssue(bucketIssues, {
|
|
349
351
|
key: "no_patterns",
|
|
350
352
|
severity: cov.priority === "MANDATORY" ? "impossible" : "warning",
|
|
351
|
-
|
|
353
|
+
message: "no shift patterns overlap this time",
|
|
352
354
|
suggestions: [
|
|
353
355
|
"Add shift patterns that overlap this coverage window",
|
|
354
356
|
"Adjust the coverage window to match available shifts",
|
|
@@ -374,7 +376,7 @@ export class ModelBuilder {
|
|
|
374
376
|
recordBucketIssue(bucketIssues, {
|
|
375
377
|
key: "no_assignable",
|
|
376
378
|
severity: cov.priority === "MANDATORY" ? "impossible" : "warning",
|
|
377
|
-
|
|
379
|
+
message: "no eligible team members can work overlapping shift patterns",
|
|
378
380
|
suggestions: [
|
|
379
381
|
"Adjust shift pattern role requirements",
|
|
380
382
|
"Add shift patterns that eligible team members can work",
|
|
@@ -399,7 +401,7 @@ export class ModelBuilder {
|
|
|
399
401
|
recordBucketIssue(bucketIssues, {
|
|
400
402
|
key: "mandatory_time_off",
|
|
401
403
|
severity: cov.priority === "MANDATORY" ? "impossible" : "warning",
|
|
402
|
-
|
|
404
|
+
message: "all eligible team members are on mandatory time off",
|
|
403
405
|
suggestions: [
|
|
404
406
|
"Adjust mandatory time-off requests",
|
|
405
407
|
"Add more team members with the required role or skills",
|
|
@@ -410,7 +412,7 @@ export class ModelBuilder {
|
|
|
410
412
|
recordBucketIssue(bucketIssues, {
|
|
411
413
|
key: `insufficient:${availableMembers.size}`,
|
|
412
414
|
severity: cov.priority === "MANDATORY" ? "impossible" : "warning",
|
|
413
|
-
|
|
415
|
+
message: `only ${availableMembers.size} team members available, need ${cov.targetCount}`,
|
|
414
416
|
suggestions: [
|
|
415
417
|
"Add more team members with the required role or skills",
|
|
416
418
|
`Reduce coverage target to ${availableMembers.size}`,
|
|
@@ -447,29 +449,29 @@ export class ModelBuilder {
|
|
|
447
449
|
comparator: ">=",
|
|
448
450
|
day: cov.day,
|
|
449
451
|
timeSlot: formatMinutes(bucketStart),
|
|
450
|
-
|
|
451
|
-
|
|
452
|
+
roleIds: cov.roleIds,
|
|
453
|
+
skillIds: cov.skillIds,
|
|
452
454
|
context: {
|
|
453
455
|
days: [cov.day],
|
|
454
456
|
memberIds: eligibleMembers.map((e) => e.id),
|
|
455
457
|
},
|
|
456
|
-
|
|
458
|
+
group: cov.group,
|
|
457
459
|
});
|
|
458
460
|
}
|
|
459
461
|
for (const issue of bucketIssues.values()) {
|
|
460
462
|
const ranges = bucketStartsToRanges(issue.bucketStarts, bucket, covEnd).map((range) => `${formatMinutes(range.start)}-${formatMinutes(range.end)}`);
|
|
461
463
|
if (ranges.length === 0)
|
|
462
464
|
continue;
|
|
463
|
-
const
|
|
465
|
+
const message = `Coverage for ${coverageLabel} on ${cov.day} (${ranges.join(", ")}) cannot be met: ${issue.message}.`;
|
|
464
466
|
if (issue.severity === "impossible") {
|
|
465
467
|
this.reporter.reportCoverageError({
|
|
466
468
|
day: cov.day,
|
|
467
469
|
timeSlots: ranges,
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
470
|
+
roleIds: cov.roleIds,
|
|
471
|
+
skillIds: cov.skillIds,
|
|
472
|
+
message,
|
|
471
473
|
suggestions: issue.suggestions,
|
|
472
|
-
|
|
474
|
+
group: cov.group,
|
|
473
475
|
});
|
|
474
476
|
}
|
|
475
477
|
// Note: soft coverage warnings are tracked via trackConstraint and reported post-solve
|