With several obligation solutions, the planner ran the whole preference chain on candidate solutions as previews: every solution after a complete enumeration, or the eight best-ranked after a truncated window search. It now orders all solutions by the existing pre-preference ranking (active objectives, then tie-breaks, of each obligation-only plan; equal ranks keep enumeration order) and runs the preference stages once, on the first. If that run ends with earned leave available only after its final merged time-off period, the stages restart on the next solution, so a request that planned before never becomes a conflict. The loaded year drops from 43 to 5.5 seconds and typed obligations from 3.3 to 0.1 seconds, with byte-identical benchmark plans. Over the 5,000 seeded requests, 23 plans become worse (9 on a real objective) and 1 better; over 50 loaded-year-sized requests, 4 become worse on a real objective. ADR 0010 records the decision; docs/benchmark/issue-302.md records the comparison and the opt-in runner that reproduces it. The frozen-engine check fails until the frozen engine is rebuilt from this commit in the next one. Part of #302. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 lines
2.3 KiB
Markdown
10 lines
2.3 KiB
Markdown
# Rank Obligation Solutions Before Preferences
|
|
|
|
Accepted for [issue #302](https://gitea.nessar.fr/nessar/vac-optimizer/issues/302), after the analysis in [issue #288](https://gitea.nessar.fr/nessar/vac-optimizer/issues/288).
|
|
|
|
A search change may change planning outcomes when it is deliberate, isolated in one reviewed commit and recorded. The frozen engine of the [frozen-engine differential check](../benchmark/README.md#frozen-engine-differential-check) is rebuilt from that commit, in its own commit, and the benchmark documentation records a reproducible before/after comparison over the check's seeded requests, including every request that became worse on a real objective. Exact optimizations still keep every outcome, stable explanation and final-state snapshot identical to the frozen engine.
|
|
|
|
When obligation rules can be satisfied jointly in several ways, the planner orders the obligation solutions by its pre-preference ranking: the active objectives, then the tie-breaks, evaluated on the obligation-only plan. Equal ranks keep enumeration order. The planner runs the preference stages once, on the first solution, instead of previewing the preference stages on every candidate solution. This removes a cost that grew with the number of solutions, and trades away the cases where a lower-ranked solution ends better after the preferences: over the 5,000 seeded requests, 23 plans became worse, 9 of them on a real objective, and 1 became better; over 50 loaded-year-sized requests, 4 became worse on a real objective ([comparison record](../benchmark/issue-302.md#comparison-record)). If the run on the chosen solution ends in an earned-entitlement chronology conflict, the planner tries the next solution in that order, and reports a conflict only when every solution fails.
|
|
|
|
The Bun benchmark targets, measured with the benchmark protocol on the development machine, are 10 seconds for the loaded year, 5 seconds for multi-year horizons and 3 seconds for every other scenario. They are development targets, not reference-phone limits. This revisits the five-second target of [ADR 0003](0003-constraint-first-lexicographic-planning.md); [ADR 0006](0006-user-selected-horizons-and-cancellable-calculations.md) already removed its automatic cutoff and sets no validated limit for multi-year horizons.
|