Deux postes en même temps possibles, malus dans le solveur
ci-cd / build-backend (push) Successful in 45s
ci-cd / build-frontend (push) Successful in 24s
ci-cd / deploy (push) Successful in 27s

Le chevauchement de créneaux (SHIFT_OVERLAP) n'est plus bloquant : il
remonte en avertissement et l'affectation est enregistrée. Le solveur ne
l'exclut plus mais applique un fort malus au bénévole déjà affecté
ailleurs au même moment (proposé seulement sous l'effectif minimal, faute
de mieux) ; il n'est jamais reproposé sur un créneau où il est déjà.
Même malus dans le classement par défaut du panneau et dans le mock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AdamThomas
2026-10-25 10:38:52 +01:00
co-authored by Claude Opus 5.5
parent d0fa1e109c
commit 0f904f5e3d
11 changed files with 114 additions and 37 deletions
+20 -7
View File
@@ -536,13 +536,19 @@ Module isolé `backend/app/validation/`. **Tests écrits avant l'implémentation
un cas passant + un cas déclenchant par règle.
Retourne une liste de violations typées `BLOCKING` ou `WARNING` (aucune règle
`WARNING` aujourd'hui : le type reste prévu par le contrat de l'API). Chaque
`BLOCKING` aujourd'hui depuis l'étape 44 : le type reste prévu par le contrat
de l'API). Chaque
violation est un **code** + des paramètres structurés, jamais une phrase (la
traduction se fait via `locales/fr.json`).
| Code | Sévérité | Règle |
|---------------------------|------------|-------------------------------------------------------------|
| `SHIFT_OVERLAP` | `BLOCKING` | Deux créneaux d'un même bénévole se chevauchent. |
| `SHIFT_OVERLAP` | `WARNING` | Deux créneaux d'un même bénévole se chevauchent. |
**Deux postes en même temps sont permis** (étape 44) : le chevauchement ne
bloque plus l'affectation, il remonte en avertissement dans le panneau
d'affectation (« Déjà affecté sur … au même moment »). C'est le solveur qui
évite de le proposer (malus, § 4).
**Aucune limite de temps dans la journée** (étape 42) : plus de cumul
journalier maximal (`DAILY_HOURS_EXCEEDED`, ex-8 h), plus de repos minimal
@@ -597,10 +603,15 @@ affectation n'est jamais persistée par cet endpoint.
recalcule à la demande (rien n'est mis en cache côté serveur) et renvoie
`{ suggestions: [{ shift_id, volunteer_ids }], shifts_considered,
volunteers_considered }`, `volunteer_ids` du meilleur au moins bon.
- **Seule contrainte dure** : aucun chevauchement de créneaux pour un même
bénévole (règle `SHIFT_OVERLAP`) — à la fois contre les affectations déjà
décidées et entre deux créneaux nouvellement suggérés. L'effectif idéal est
un plafond dur (le solveur ne sur-staffe jamais).
- **Contraintes dures** : jamais deux créneaux qui se chevauchent suggérés au
même bénévole dans un même calcul ; jamais un bénévole suggéré sur un
créneau où il est déjà affecté. L'effectif idéal est un plafond dur (le
solveur ne sur-staffe jamais). Un bénévole **déjà affecté à un autre poste
au même moment** reste proposable (la validation ne fait qu'avertir, § 3)
mais avec un fort malus (`_ALREADY_BUSY_PENALTY`, 300 — plus que tous les
bonus cumulés et que la base d'un créneau au-dessus de son minimum) : en
pratique, il n'est proposé que pour un créneau sous son effectif minimal,
et seulement faute de mieux.
- Tout le reste est un terme de l'objectif (maximisation), pas une
contrainte — cohérent avec le moteur de validation, qui reste seul juge au
moment où la coordination confirme une affectation :
@@ -632,7 +643,8 @@ affectation n'est jamais persistée par cet endpoint.
**Classement par défaut, sans lancer le solveur** — `frontend/src/lib/planning.ts`
(`candidateChanceScore`) porte une version simplifiée du même scoring (vœu de
poste, conflit d'artiste, compagnons souhaités déjà présents sur le créneau,
charge déjà affectée ; pas de chaînage). C'est ce
déjà affecté ailleurs au même moment, charge déjà affectée ; pas de
chaînage). C'est ce
qui classe la colonne des candidats dans le panneau d'affectation dès
l'ouverture du détail d'un poste, sans requête réseau. Si le solveur a
effectivement tourné (`POST .../solver/suggestions`), ses bénévoles retenus
@@ -904,6 +916,7 @@ Aucune dépendance locale requise hors Docker.
| 41 | Début de « journée de festival » indépendant de l'ordre des créneaux types : début du plus long creux qu'ils laissent sur 24 h (`validation.engine.day_boundary`, miroir `lib/planning.ts:dayBoundaryFromDayParts`). Avant, un créneau type d'ordre 0 déplacé à 21:00 faisait commencer chaque journée des grilles (besoins, vue bénévoles) à 21:00, jusqu'à 03:00 le lendemain | à valider |
| 42 | Retrait des disponibilités déclarées (table `availability` supprimée, migration `0022` ; API, éditeur de la fiche bénévole, badge de couverture du panneau d'affectation, colonnes des statistiques) et des limites de temps dans la journée (règles `OUTSIDE_AVAILABILITY`, `DAILY_HOURS_EXCEEDED`, `INSUFFICIENT_REST`, malus de disponibilité et de repos du solveur, filtre « Surchargés » de la vue bénévoles) — seul le chevauchement reste contrôlé. La case « je suis disponible » du formulaire public est conservée | à valider |
| 43 | Logs de diagnostic (ligne par requête avec route modèle, statut, durée, code d'erreur et compte ; identifiant `X-Request-ID` repris dans chaque log et affiché à l'utilisateur sous une erreur ; exceptions non gérées tracées ; journal d'accès d'uvicorn coupé car il exposait le jeton du formulaire public ; logs métier SumUp, connexion, import, plannings, solveur) et rapprochement SumUp ↔ bénévole : nom du bénévole dans la description du checkout (pas de métadonnées libres dans l'API), code et identifiant de transaction enregistrés au paiement (`Volunteer.fee_transaction_code`/`fee_transaction_id`, migration `0023`), affichés sur la fiche et dans l'export Excel | à valider |
| 44 | Un bénévole peut être affecté à deux postes en même temps : `SHIFT_OVERLAP` passe de `BLOCKING` à `WARNING` (affectation enregistrée, avertissement affiché) ; le solveur ne l'exclut plus mais lui applique un fort malus (`_ALREADY_BUSY_PENALTY`) — proposé seulement pour un créneau sous son minimum, faute de mieux — repris dans le classement par défaut du panneau et dans le mock | à valider |
**Reste hors périmètre** (modèle prêt à les accueillir) : SMS, constructeur
de formulaire, pointage sur site, IA. Frontend : squelette + 6 écrans +