perf(planning): validate each optimization configuration once

Every objective evaluation revalidated its optimization configuration.
Rule normalization already validates each configuration once per
calculation and keeps its own copy, the presets are constant, and the
summed variant of a valid configuration stays valid, so the core now
scores them without revalidating. evaluateOptimization still validates
for other callers.

Part of #302.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
nessarandClaude Opus 5.5 committed 2026-10-18 20:03:22 +02:00
1 parent b7ec411c92
commit 9d9404c3db
4 files changed
+43 -14

No files matched your search

+19
View File
@@ -89,3 +89,22 @@ largest gap stayed between 58 and 67 ms in every run. Cancelling `three years`
one second after the request returned the retained plan 3.9 to 4.0 ms after the
cancel message reached the worker, against 7.2 to 8.7 ms before. This is desktop
evidence, not a reference-phone measurement.
### Validate each optimization configuration once
Every objective evaluation revalidated its optimization configuration against
the catalog. Rule normalization already validates each configuration once per
calculation and keeps its own copy. The presets are constant, and the summed
variant of a valid configuration is valid because every catalog row accepts
the sum. The core therefore scores them without revalidating;
`evaluateOptimization` still validates for its other callers.
| Scenario | Before (s) | After (s) |
| --------------------------- | ---------: | --------: |
| representative year | 0.35 | 0.30 |
| typed obligations | 4.03 | 3.46 |
| chained earned entitlements | 0.01 | 0.01 |
| deliberate conflict | 0.00 | 0.00 |
| loaded year | 48.12 | 44.03 |
| two years | 1.04 | 0.96 |
| three years | 3.35 | 3.03 |
+13 -12
View File
@@ -24,7 +24,7 @@ import { addPlanningYears, isPlanningDate, planningDateInParis } from './plannin
import {
compareObjectiveLists,
compareObjectiveValues,
evaluateOptimization,
evaluateValidatedOptimization,
firstDifferentObjective,
OPTIMIZATION_PRESETS,
validateOptimizationConfiguration,
@@ -1896,11 +1896,12 @@ function reconcileOptimizationProvenance(
// Another tied maximum can mask a placement's improvement to its own element.
observations ??= optimizationObservations(state, context);
const withoutObservations = optimizationObservations(withoutState, context);
// Every catalog row accepts the sum, so this stays a valid configuration.
const summed = { ...rule.configuration, aggregation: 'sum' as const };
const summedComparison = compareObjectiveValues(
objective.direction,
evaluateOptimization(summed, observations),
evaluateOptimization(summed, withoutObservations),
evaluateValidatedOptimization(summed, observations),
evaluateValidatedOptimization(summed, withoutObservations),
);
contributes =
summedComparison < 0 ||
@@ -1930,14 +1931,14 @@ function supportingCalendarBenefit(
): boolean {
const observations = optimizationObservations(after, context);
return (
evaluateOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, observations) >
evaluateOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, before) ||
evaluateOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, observations) >
evaluateOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, before) ||
evaluateOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, observations) <
evaluateOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, before) ||
evaluateOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, observations) <
evaluateOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, before)
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, observations) >
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, before) ||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, observations) >
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, before) ||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, observations) <
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, before) ||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, observations) <
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, before)
);
}
@@ -2654,7 +2655,7 @@ function* optimizeRulePlacements(
item.configuration.measure === 'public-holiday-week-value' &&
item.configuration.direction === 'maximize'
)
return evaluateOptimization(item.configuration, {
return evaluateValidatedOptimization(item.configuration, {
leaveBalances: [],
publicHolidayWeeks: context.holidayWeeks.map(({ value }) => ({
value,
+3 -2
View File
@@ -1,7 +1,7 @@
import { isWorkingPlanningDate } from './declared-non-working-days';
import type { TimeOffPeriod } from './leave-planner.core';
import {
evaluateOptimization,
evaluateValidatedOptimization,
type OptimizationConfiguration,
type OptimizationObservations,
} from './planning-objectives';
@@ -683,11 +683,12 @@ export function workSequenceDates(state: ObservedPlan, context: ObservationCalen
}));
}
/** The configuration was validated when the calculation's rules were normalized. */
export function evaluateStateOptimization(
configuration: OptimizationConfiguration,
state: ObservedPlan,
context: ObservationCalendar,
observations = optimizationObservations(state, context),
): number {
return evaluateOptimization(configuration, observations);
return evaluateValidatedOptimization(configuration, observations);
}
+8
View File
@@ -212,6 +212,14 @@ export function evaluateOptimization(
): number {
const validation = validateOptimizationConfiguration(configuration);
if (!validation.valid) throw new Error(`${validation.code}: ${validation.message}`);
return evaluateValidatedOptimization(configuration, observations);
}
/** Scores a configuration that {@link validateOptimizationConfiguration} already accepted. */
export function evaluateValidatedOptimization(
configuration: OptimizationConfiguration,
observations: OptimizationObservations,
): number {
const selected = (condition: OptimizationCondition) =>
configuration.conditions.includes(condition);
let values: number[];