alpha
Login
or
Join now
russ.fugl.dev
/
levelset
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
A functional toolkit for authoring resource-leveling algorithms — compose constraint and scoring blocks, enumerate feasible schedules
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
levelset
/
src
/
level-core
/
at
main
1 folder
11 files
Russ T. Fugal
feat(level-blocks): add Gate feasibility-gate collector block
6d ago
90dd0440
search
feat(leveling): named, toggleable constraint instances with attributed failures Every Constraint variant gains optional instance metadata (spaCy's factory-vs-instance-name split): `name` for attribution and `enabled` as the present-but-inactive lever. serialSGS skips `enabled: false` constraints and records the skip in a disabledConstraints annotation so reports can tell disabled from absent; deadline violations now carry the named constraint instance that bound instead of a synthesized copy. Search backends can declare which kinds they enforce via supports().
1 week ago
analyzeRun.ts
feat(level-blocks): add Gate feasibility-gate collector block Gate keeps only schedules that satisfy every limit, where a limit bounds one scorer's value by max and/or min (an absent bound is unbounded on that side, never Infinity; at least one bound is required per limit). When at least one schedule passes, the passing set (order-preserving) is the result. When none pass, fallback decides: "none" yields the empty set; "leastBad" yields the single least-bad schedule. Least-bad compares schedules lexicographically by per-limit violation in the limits' declared order (first limit = binding). Violations of different scorers are not commensurable, so they are prioritized by order, never summed across scorers. Extends the Collector ADT with the Gate variant and GateLimit — the exhaustive collectorScorerRefs / collectors() switches (guarded by assertNeverCollector) force both dispatch sites to handle it. Adds the pure selectGate engine, levelset.Gate.v1 registration, leveling.ts re-exports, and README. Gate execution collects the stream once and filters/falls back in memory.
6 days ago
calendarDays.ts
feat(level-core): O(1) working-day calendar arithmetic Working-day arithmetic over the bitmap calendar. countWorkingDays is an O(1) prefix-sum query (cumWorking[hi] - cumWorking[lo]) — the dominant operation in the scheduler. day-to-date conversion is DST-safe and round-trips exactly. resolveWorkingCalendar centralizes the per-task → project-default → first-available → throw fallback chain the schedulers share, and a null calendar yields a synthetic all-working calendar so a project with no calendars still resolves. Forward walks clamp at the horizon instead of running off the end of the bitmap.
2 months ago
currentSchedule.ts
feat(level-core): currentSchedule and materialize bridges The two bridges between a ResolvedProject and a Schedule. currentSchedule reads existing source dates into Schedule form, so round-trip identity holds before any search has run — the no-search-yet case is first-class. materialize goes the other way, carrying the entire source ProjectFile through and overriding only start/finish/duration for tasks present in the schedule, so a partial schedule passes the rest through untouched. Durations are re-emitted in the source unit via the project's unit conversions, and both honor per-task calendars. A mode change throws until mode selection is implemented.
2 months ago
errors.ts
feat(leveling): coded static run analysis (analyzeRun) analyzeRun walks constraints' and scorers' declared references against a ResolvedProject before any search runs — spaCy's analyze_pipes + validate_attrs applied to leveling. Issues carry stable LEVELSET_E###/W### codes, error/warning severity, and attribute to the named instance: unknown resource/task/unit/calendar refs, deadline-vs-release and deadline-vs-duration infeasibility, precedence and unit-precedence cycles, inverted PeakCap windows, kinds the chosen search won't enforce (via supports()), empty governed sets, and duplicate instance names. Scorers gain optional reads metadata so a scorer referencing a missing entity is caught instead of silently optimizing on a constant zero; the four shipping scorer blocks now declare it.
1 week ago
materialize.ts
feat(level-core): ModeSelection multi-mode placement in serial-SGS Replace the "mode-change not yet supported" stub with greedy multi-mode RCPSP placement. Each task placement now tries a list of candidate modes (preferred/home crew first) and takes the first feasible one, so a borrow mode only wins when it lets the task start strictly earlier. The chosen mode's resource demand is the authoritative load for the task: serialSGS emits it as Schedule.realizedAssignments, and both buildResourceLoad and materialize override the task's static assignments with the realized demand under a single rule (a task present in realizedAssignments is always loaded from there, never from resolved).
1 month ago
profiles.ts
feat(leveling): shared schedule profiles read by all scoring blocks Adds level-core/profiles.ts — a per-Schedule memoized provider (WeakMap, safe because Schedule is immutable by contract) for the derived data every scorer needs: per-resource day histograms, per-unit open spans, task lookup. Scoring blocks become listeners that read the shared slot instead of each recomputing, turning O(scorers × days × tasks) per candidate into O(days × tasks). This also fixes an inconsistency: ConcurrentResourceCost and OpenUnitPenalty's discipline filter previously read static assignments only, ignoring realizedAssignments for ModeSelection tasks — the shared histogram applies the single realized-override rule everywhere.
1 week ago
resolveCalendar.ts
feat(level-core): resolveCalendar pipeline entry resolveCalendar turns a ProjectFile into a ResolvedProject — the entry point of the leveling pipeline. The epoch is opts.epoch ?? statusDate ?? earliest task start, and resolving with no available origin throws rather than silently defaulting. capacityPerDay is derived as work-unit-hours per day; calendars are built into a per-id map with a synthetic Mon–Fri fallback; durations and lags are converted to working-day units from the project's minutes-per-day and minutes-per-week. WorkUnit definitions are resolved into the by-id registry here, and duplicate unit ids are rejected at resolve time so a duplicate cannot diverge the greedy and MiniZinc objectives downstream.
2 months ago
scheduleStream.ts
fix(level-blocks): validate collector refs off the compiled spec; exhaustive dispatch Extract collector scorer refs from the compiled Collector spec (via the block's pure apply()) instead of duck-typing parsed params as {by, over}: a custom collector naming its refs under a different param no longer skips layer-1 validation, and a non-array by/over can't surface as a raw TypeError. Distinguish a missing scorer from a disabled one in the layer-2 runFromConfig check. Convert both collector dispatch ternaries to exhaustive switches ending in assertNeverCollector so a future variant is a compile error. Fix stale docs (collectors labeled by, not keyed by; single-scorer Pareto keeps all co-best; ES2019 sort stability).
6 days ago
types.ts
feat(level-blocks): add Gate feasibility-gate collector block Gate keeps only schedules that satisfy every limit, where a limit bounds one scorer's value by max and/or min (an absent bound is unbounded on that side, never Infinity; at least one bound is required per limit). When at least one schedule passes, the passing set (order-preserving) is the result. When none pass, fallback decides: "none" yields the empty set; "leastBad" yields the single least-bad schedule. Least-bad compares schedules lexicographically by per-limit violation in the limits' declared order (first limit = binding). Violations of different scorers are not commensurable, so they are prioritized by order, never summed across scorers. Extends the Collector ADT with the Gate variant and GateLimit — the exhaustive collectorScorerRefs / collectors() switches (guarded by assertNeverCollector) force both dispatch sites to handle it. Adds the pure selectGate engine, levelset.Gate.v1 registration, leveling.ts re-exports, and README. Gate execution collects the stream once and filters/falls back in memory.
6 days ago
weeklyProfile.ts
feat(leveling): shared schedule profiles read by all scoring blocks Adds level-core/profiles.ts — a per-Schedule memoized provider (WeakMap, safe because Schedule is immutable by contract) for the derived data every scorer needs: per-resource day histograms, per-unit open spans, task lookup. Scoring blocks become listeners that read the shared slot instead of each recomputing, turning O(scorers × days × tasks) per candidate into O(days × tasks). This also fixes an inconsistency: ConcurrentResourceCost and OpenUnitPenalty's discipline filter previously read static assignments only, ignoring realizedAssignments for ModeSelection tasks — the shared histogram applies the single realized-override rule everywhere.
1 week ago
weightedScorer.ts
docs: document the declarative run layer; fix stale OpenUnitPenalty example README gains a 'Declarative runs (RunConfig)' section covering the versioned registry, analyzeRun, enabled levers, weighted/lexicographic objectives, shared profiles, and provenance; the OpenUnitPenalty example now matches the current unitIds schema. CHANGELOG entries added.
1 week ago