feat(planning): make optimization rules composable

Evaluate explicit, ordered optimization configurations through one shared
planner model. Add six editable presets, durable one-time legacy migration,
and consistent completed and cancelled plan provenance.

Include functional regressions and the agreed trace-removal and benchmark
follow-up specs. Performance acceptance remains in #78 under unchanged gates.

Refs: #70, #71, #72, #73, #74, #75, #76, #77, #78
This commit is contained in:
nessar committed 2026-10-10 16:07:34 +02:00
1 parent 209bf05f36
commit 6811142299
45 files changed
+8795 -3075

No files matched your search

@@ -1,26 +1,34 @@
# 06 — Migrer les règles sauvegardées et valider l’ensemble
# 06 — Migrer les règles sauvegardées et valider le comportement fonctionnel
**GitLab:** [#75](https://gitlab.nessar.fr/nessar/vac-optimizer/-/work_items/75)
**What to build:** Au prochain chargement, l’utilisateur retrouve ses règles et les six anciens comportements du moteur sous forme de préréglages actifs. Il peut ensuite les modifier ou les supprimer durablement, et l’ensemble est validé avant sa mise en service.
Blocked by: #74
**Blocked by:** 05 — Piloter l’économie des jours d’appoint.
Les exigences de performance sont transférées à #78. La clôture de ce ticket dépend de la migration et de la validation fonctionnelle, pas du passage du protocole de performance. Les limites du benchmark et les contrôles CI restent inchangés.
**Status:** ready-for-agent
## What to build
Livraison intégrée de l’ADR 0008 sur la branche commune aux tickets 01 à 05. La préservation des données enregistrées et la migration unique ont été explicitement approuvées : ne pas réinitialiser les données pour simplifier le changement de contrat.
Au prochain chargement, l’utilisateur retrouve ses règles et les six anciens comportements du moteur sous forme de préréglages actifs. Il peut ensuite les modifier ou les supprimer durablement, et l’ensemble est validé avant sa mise en service.
- [ ] À l’initialisation, fournir les six préréglages actifs avec cet ordre : prévention des pertes, règles personnelles ou règles initiales existantes dans leur ordre, semaines fériées, gain de jours non ouvrés, séquence maximale, déficit des reprises et économie des placements d’appoint.
- [ ] Migrer une seule fois les listes enregistrées avant cette évolution, y compris une ancienne liste vide. Préserver intégralement l’identité, le nom, les paramètres, les dates, la force, l’activation et l’ordre relatif des règles existantes.
- [ ] Enregistrer de façon cohérente les règles migrées et le fait que la migration a eu lieu. Ne pas considérer l’opération terminée si les règles n’ont pas été sauvegardées ; gérer les échecs de stockage sans écraser les données utilisateur valides.
- [ ] Les chargements suivants ne dupliquent pas les préréglages, ne rétablissent pas ceux supprimés et ne réactivent pas ceux désactivés. Une liste vidée volontairement après migration reste vide. Les nouveaux utilisateurs reçoivent directement la nouvelle configuration initiale.
- [ ] La restauration et la migration précèdent le premier calcul utilisant ces données. L’utilisateur peut immédiatement éditer les six configurations avec les mêmes commandes que ses règles personnelles et retrouver ses choix après rechargement.
- [ ] Vérifier de bout en bout les six préréglages dans l’ordre initial, leurs réordonnancements et leurs désactivations. Une configuration personnelle différente d’un préréglage traverse édition, sauvegarde, worker, calcul et explication ; les paramètres ont un effet vérifiable.
- [ ] La liste entièrement désactivée, la liste supprimée après migration et une requête directe sans règle active n’ajoutent aucun congé malgré des soldes à risque. Les jours déclarés, les quantités restantes et les raisons pertinentes restent cohérents.
- [ ] Les obligations conjointes, les conditions des congés gagnés et les contraintes de validité sont respectées pour tout ordre des préférences. Une annulation restitue le meilleur plan valide disponible et sa trace originale selon la configuration soumise.
- [ ] Les explications et analyses contrefactuelles utilisent le même ordre actif que le calcul. Modifier les règles durant le calcul ou pendant la livraison tardive de la trace ne change pas ses résultats. Les anciens objectifs implicites n’interviennent plus dans aucune comparaison ou protection.
- [ ] Les fixtures et scénarios de benchmark qui dépendaient d’une optimisation implicite demandent explicitement les préréglages nécessaires. Les tests d’absence de règles n’utilisent aucun défaut implicite pour retrouver l’ancien résultat.
- [ ] Exécuter les suites concernées de règles, interface, moteur, worker réel, explication, annulation et estimation de durée, ainsi que les contrôles habituels de compilation et de qualité. Vérifier les scénarios de benchmark existants, dont les horizons longs, pour le déterminisme, la réactivité et les durées, sans introduire de délai d’arrêt automatique.
- [ ] La vérification commune des tickets 01 à 06 est la condition de mise en service. Documenter les choix finaux du contrat composable et les limites mesurées ; ne laisser ni double moteur, ni chemin historique implicite, ni adaptateur provisoire ajouté pour rendre les étapes indépendamment livrables.
Livraison intégrée de l’ADR 0008 sur la branche commune aux tickets #70, #71, #72, #73, #74. La préservation des données enregistrées et la migration unique ont été explicitement approuvées : ne pas réinitialiser les données pour simplifier le changement de contrat.
## Acceptance criteria
- [x] À l’initialisation, fournir les six préréglages actifs avec cet ordre : prévention des pertes, règles personnelles ou règles initiales existantes dans leur ordre, semaines fériées, gain de jours non ouvrés, séquence maximale, déficit des reprises et économie des placements d’appoint.
- [x] Migrer une seule fois les listes enregistrées avant cette évolution, y compris une ancienne liste vide. Préserver intégralement l’identité, le nom, les paramètres, les dates, la force, l’activation et l’ordre relatif des règles existantes.
- [x] Enregistrer de façon cohérente les règles migrées et le fait que la migration a eu lieu. Ne pas considérer l’opération terminée si les règles n’ont pas été sauvegardées ; gérer les échecs de stockage sans écraser les données utilisateur valides.
- [x] Les chargements suivants ne dupliquent pas les préréglages, ne rétablissent pas ceux supprimés et ne réactivent pas ceux désactivés. Une liste vidée volontairement après migration reste vide. Les nouveaux utilisateurs reçoivent directement la nouvelle configuration initiale.
- [x] La restauration et la migration précèdent le premier calcul utilisant ces données. L’utilisateur peut immédiatement éditer les six configurations avec les mêmes commandes que ses règles personnelles et retrouver ses choix après rechargement.
- [x] Vérifier de bout en bout les six préréglages dans l’ordre initial, leurs réordonnancements et leurs désactivations. Une configuration personnelle différente d’un préréglage traverse édition, sauvegarde, worker, calcul et explication ; les paramètres ont un effet vérifiable.
- [x] La liste entièrement désactivée, la liste supprimée après migration et une requête directe sans règle active n’ajoutent aucun congé malgré des soldes à risque. Les jours déclarés, les quantités restantes et les raisons pertinentes restent cohérents.
- [x] Les obligations conjointes, les conditions des congés gagnés et les contraintes de validité sont respectées pour tout ordre des préférences. Une annulation restitue le meilleur plan valide disponible selon la configuration soumise. La suppression de la trace originale est suivie séparément dans #76.
- [x] Les explications et analyses contrefactuelles utilisent le même ordre actif que le calcul. Modifier les règles durant le calcul ou pendant la livraison des explications ne change pas ses résultats. Les anciens objectifs implicites n’interviennent plus dans aucune comparaison ou protection.
- [x] Les fixtures et scénarios de benchmark qui dépendaient d’une optimisation implicite demandent explicitement les préréglages nécessaires. Les tests d’absence de règles n’utilisent aucun défaut implicite pour retrouver l’ancien résultat.
- [x] Exécuter les suites fonctionnelles concernées de règles, interface, moteur, worker réel, explication, annulation et estimation de durée, ainsi que les contrôles habituels de compilation et de qualité. Préserver les assertions de résultat et de déterminisme des fixtures, y compris les horizons longs. L’exécution complète du protocole de benchmark, la réactivité mesurée, les durées, la consommation mémoire et les optimisations nécessaires relèvent de #78 et ne conditionnent plus la clôture de #75. Ne pas introduire de délai d’arrêt automatique.
- [x] La vérification fonctionnelle commune des tickets #70, #71, #72, #73, #74, #75 est la condition de clôture fonctionnelle de ce ticket. Documenter les choix finaux du contrat composable ; ne laisser ni double moteur, ni chemin historique implicite, ni adaptateur provisoire ajouté pour rendre les étapes indépendamment livrables. Les limites de performance mesurées et leur amélioration sont suivies dans #78 ; les exigences habituelles de CI et de mise en service ne sont pas abaissées.
**Vérification de bout en bout :** Partir d’une ancienne sauvegarde contenant des règles personnalisées et désactivées, charger la nouvelle version, vérifier le premier calcul, modifier ou supprimer des préréglages, recharger et constater la conservation exacte des choix.
## Blocked by
- #74 — Piloter l’économie des jours d’appoint
@@ -0,0 +1,29 @@
# 01 — Remove the original decision trace end to end
**GitLab:** [#76](https://gitlab.nessar.fr/nessar/vac-optimizer/-/work_items/76)
## What to build
Calculations no longer record, retain or deliver an original decision trace. Users receive the displayed plan, its stable explanation and remaining-balance reasons, and can still analyze an alternative date for a displayed placement. The advanced trace explorer, historical calendar replay and historical attempt lists disappear completely.
This is one cohesive contract replacement across the planner, calculation service, workers and UI. Use a scoped integration branch containing the existing #70–#75 implementation work; #75's final verification must not be treated as a prerequisite for removing tracing. The shared functional integration verification gate is #77 — Verify integrated planning after trace removal. Benchmark performance acceptance and any required optimization are owned by #78; performance is no longer a completion gate for #75.
## Acceptance criteria
- [ ] Delete trace capture, materialization, candidate-history retention, replay, historical eligibility and caches or evidence pools used exclusively for tracing. Remove observational work while preserving search breadth, allocation previews, rule priority and incumbent selection.
- [ ] Replace trace contracts directly and update all callers in the same change. Remove trace promises, callbacks, public types, streaming messages, status indexes, acknowledgements and backpressure queues across real-worker, browser-fallback and server-side calculation paths. Do not retain debug toggles, compatibility adapters, empty trace results or replacement history.
- [ ] Preserve immutable calculation, valid-incumbent and final-state snapshots needed by retained behavior. Rename retained state contracts independently of tracing; do not delete cancellation or explanation data because it currently has a trace-related name.
- [ ] Retain stable plan explanations, including placement entitlement and rule provenance, objective contributions and the first decisive objective, time-off periods, remaining balances and concrete non-placement reasons. Preserve invalid-input and conflict explanations and incomplete-calculation behavior.
- [ ] Keep alternative-date analysis for a displayed placement using the captured calculation configuration, the same entitlement, leave type and provenance, and fixed unrelated placements. Remove analysis based on historical decisions; hypothetical analysis never mutates the displayed plan.
- [ ] Remove the advanced trace panel, replay controls, historical calendar overlays and candidate statuses. Remove remaining-balance historical attempt lists, pagination and loading state while retaining classification, validity, earned-entitlement source and truthful displayed-plan reasons. Leave no dead controls or trace-unavailable messages.
- [ ] Preserve explicit cancellation before and after a valid incumbent, best-valid-partial-plan recovery, heartbeat monitoring, stale-response rejection and the existing technical-budget contract. Interrupted calculations do not imply an obligation conflict. Explanation delivery failures do not discard an available outcome, and planning workers do not wait for trace acknowledgements after required delivery.
- [ ] Preserve completed placements, balances, objective values, joint obligation satisfaction, earned-entitlement chronology, deterministic tie-breaking, user-controlled priorities and empty-active-rule behavior. Do not reset saved entitlements, declarations or rule configurations or change the existing migration.
- [ ] Update component styles and translations across supported languages, preserving keyboard access and accessibility for retained explanations, calendar interactions and date analysis.
- [ ] Adapt existing planner, service, worker, explanation, cancellation, calendar and remaining-balance tests. Delete tests exclusively for tracing while retaining assertions for supported behavior in mixed tests. Update benchmark callers, output and assertions to remove obsolete trace requirements without reducing scenarios or changing performance gates.
- [ ] Supersede trace-specific requirements in the domain vocabulary and explanation/cancellation ADRs, and update benchmark documentation. Preserve the remaining planning contracts and document that the original decision trace and historical analysis are removed.
No new dependency, replacement telemetry, optimization-quality change, allocation-preview reduction or benchmark deadline relaxation is part of this ticket. No separate prefactor is required: retained explanation and hypothetical-analysis behavior already exists.
## Blocked by
None — can start immediately.
@@ -0,0 +1,27 @@
# 02 — Verify integrated planning after trace removal
**GitLab:** [#77](https://gitlab.nessar.fr/nessar/vac-optimizer/-/work_items/77)
Blocked by: #76
## What to build
Demonstrate that the integrated #70–#75 work retains its supported behavior after trace removal. Provide a functionally verified trace-free baseline for #78, which now owns benchmark performance acceptance and optimization. Benchmark performance is no longer a completion gate for #75 or this functional verification ticket.
## Acceptance criteria
- [ ] Review the integrated contract replacement across all calculation paths and UI callers. Confirm there is no trace recorder, trace transport, acknowledgement wait, historical replay API or retained trace-dependent control or compatibility shell.
- [ ] Compare completed outcomes and stable explanations against existing deterministic fixtures, including typed joint obligations, earned-entitlement chains, custom objective formulas, reordered priorities, two- and three-year horizons and no active optimization rules. Do not rewrite placement expectations to hide a behavioral regression.
- [ ] Verify real-worker outcome and explanation delivery, heartbeat monitoring, stale-response rejection and explanation failure handling. Exercise cancellation before any valid incumbent and after a valid incumbent, and confirm that partial outcomes remain valid without false conflicts.
- [ ] Verify displayed-placement alternative-date analysis and remaining-balance reasons without historical data. Check that captured settings remain immutable and hypothetical alternatives do not change the displayed plan.
- [ ] Run the existing full correctness, coverage, static, formatting and build checks appropriate to the integrated change, and resolve regressions within the trace-removal scope. Preserve the repository's required thresholds.
- [ ] Complete browser QA for the retained explanation, calendar, cancellation and alternative-date flows, including keyboard interaction and a narrow viewport. Confirm the advanced trace explorer, historical overlays and historical attempt controls are absent.
- [ ] Verify that benchmark callers use the supported trace-free calculation contract and retain their correctness, determinism, horizon, placement-count and completed-stage assertions. Full protocol execution, the unchanged 2 GB/300-second performance gates and any required optimization belong to #78.
- [ ] Hand off the functionally verified integrated baseline and unchanged benchmark inputs to #78. Verify retained outcome and explanation delivery without waiting for removed trace artifacts or inventing replacement trace counters.
- [ ] Keep the original #70–#75 dependency order and report the functional integration result. Apply #75’s revised functional completion criteria: a benchmark performance failure alone does not block #75 or this ticket. Outstanding benchmark requirements remain open in #78, without deadline relaxation or an automatic engine cutoff.
The removal is agreed; its measured speedup is unresolved and is now evaluated in #78. This ticket owns functional verification after trace removal. It does not itself implement performance optimizations or close other issues.
## Blocked by
- #76 — Remove the original decision trace end to end (https://gitlab.nessar.fr/nessar/vac-optimizer/-/work_items/76).
@@ -0,0 +1,38 @@
# 03 — Verify and optimize planning to meet the benchmarks from #75
**GitLab:** [#78](https://gitlab.nessar.fr/nessar/vac-optimizer/-/work_items/78)
Blocked by: #75, #77
## Parent
#75 — Performance acceptance separated from the functional migration ticket at the user's request.
## What to build
Verify the integrated planner against the benchmark requirements originally attached to #75 and apply performance optimizations when needed until those requirements pass. Start from the functionally verified implementation after original-decision-trace removal. #75 can finish on its functional requirements; performance acceptance belongs to this ticket.
If the measured baseline already passes, keep the engine unchanged and record the evidence. Otherwise profile the real failing path, apply the smallest complete improvement and verify both performance and retained planning behavior.
## Acceptance criteria
- [ ] Keep the existing seven benchmark scenarios and inputs, including the loaded year, typed joint obligations, chained earned entitlements and two- and three-year horizons. Keep explicit optimization configurations, quotas, expected outcomes, placement counts, completed-stage and determinism assertions.
- [ ] Run the real existing benchmark protocol: one warm-up and two measured repetitions per scenario, outside coverage instrumentation, with a 2 GB Node heap limit and the 300-second timeout for the full protocol. All scenarios and repetitions must finish and pass; a timeout, skipped scenario or partial report does not satisfy this ticket.
- [ ] After #76 removes tracing and #77 verifies the retained behavior, benchmark outcome and explanation delivery without obsolete trace counters, trace transfer or trace-acknowledgement waits. Do not reintroduce tracing as profiling infrastructure.
- [ ] When the baseline fails, profile CPU, allocation and repeated planning work; apply optimizations and rerun the unchanged protocol until it passes. Preserve planning semantics, rule ordering, joint obligations, earned-entitlement chronology, final-plan quality and deterministic tie-breaking. Use behavior-level or differential tests to demonstrate that caching, deduplication or pruning is valid across the relevant configurations.
- [ ] Preserve responsive browser interaction, explicit cancellation, heartbeat monitoring and best-valid-partial-plan recovery during representative and long-horizon calculations. Keep the existing duration-estimation contract and recalibrate estimates only when measured results require it.
- [ ] Do not increase the benchmark timeout or heap allowance, reduce scenarios or repetitions, weaken correctness assertions, add an automatic elapsed-time engine cutoff, or reduce search breadth or planning quality to make the protocol pass.
- [ ] Record reproducible before/after preparation, planning, total-duration, responsiveness and available peak-heap measurements, including environment and protocol. Clearly distinguish actual completed benchmark runs from diagnostics or interrupted measurements and document any reference-hardware checks that could not be performed.
- [ ] Run required correctness, coverage, static and build checks after any optimization. Preserve the captured configuration, stable explanations, displayed-placement alternative-date analysis and saved-user-data behavior.
- [ ] Update benchmark documentation with the final measured limits and evidence. Close this ticket only after the unchanged benchmark gates pass; a failed measurement remains an open performance requirement here rather than reopening #75 for performance alone.
## Existing evidence and scope
The previous real protocol timed out at 300 seconds without an out-of-memory failure. The loaded-year warm-up took about 184 seconds and its first measured repetition started about 252 seconds into the protocol. Original trace recording and repeated allocation previews were observed costs; the benefit of trace removal has not yet been measured. These are starting points for profiling, not permission to change optimization quality.
This ticket authorizes verification and implementation of performance improvements when needed. Functional migration remains in #75, trace removal in #76 and trace-removal functional verification in #77. Existing CI benchmark limits remain unchanged.
## Blocked by
- #75 — Finish the functional saved-rule migration and integrated planning validation.
- #77 — Verify integrated planning after trace removal (itself blocked by #76).