Files
benevoles/CLAUDE.md
T
AdamThomasandClaude Sonnet 5 09d7d70076 Étape 1 : squelette du projet
- Monorepo backend/ + frontend/ + deploy/
- Backend FastAPI/SQLModel/Alembic/uv qui démarre (health check, config,
  i18n, base de test PostgreSQL, pytest)
- docker-compose de développement (db + backend hot-reload)
- Chaîne CI/CD GitHub Actions : lint + tests, build image, push GHCR,
  déploiement SSH sur le VPS à chaque push main
- CLAUDE.md : vocabulaire du domaine, conventions, commandes

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
2026-09-09 22:19:58 +02:00

12 KiB
Raw Blame History

CLAUDE.md — Gestion des bénévoles

Application web de gestion des bénévoles pour un festival associatif (50–200 bénévoles, 4 jours montage/démontage inclus). Interface en français, code en anglais.

Ce fichier est le contrat de travail. Il est tenu à jour à chaque étape.


1. Vocabulaire du domaine (glossaire FR ↔ EN)

Le métier parle français, le code et la base parlent anglais. Toute entité, colonne, route et variable utilise la colonne « Code / base ».

Métier (brief) Code / base
Édition Edition
Bénévole Volunteer
Compétence Skill
Poste Position
Référent d'un poste Position.lead_account_id → Account
Créneau Shift
Disponibilité Availability
Affectation Assignment
Compte (utilisateur connecté) Account
Découpage de journée (paramétrable) DayPart (code, label, start_time, end_time, order)
Lien magique MagicLinkToken

Énumérations :

Métier Code
Statut d'affectation PROPOSED / CONFIRMED / DECLINED / CANCELLED
Origine d'affectation SOLVER / MANUAL
Verrouillée Assignment.locked: bool
Rôle COORDINATOR / LEAD / VOLUNTEER
Disponibilité (tri-état) AVAILABLE / IF_NEEDED / UNAVAILABLE
Catégorie de poste BAR / TICKETING / WELCOME / PARKING / CLEANING / CATERING / SETUP / STAGE / OTHER

Distinction critique (ne jamais confondre, ni en base ni dans l'UI)

  • Availability = une déclaration du bénévole : « je peux venir tel jour, telle demi-journée ».
  • Assignment = une décision de l'organisation : « tu tiens le bar samedi 18 h – 22 h ».

Deux tables distinctes, deux écrans distincts, deux vocabulaires distincts.


2. Modèle de domaine

  • Edition : année, start_date, end_date, retention_days (durée de rétention des données nominatives, paramétrable), liste de DayPart.
  • DayPart : découpage d'une journée propre à l'édition (matin, après-midi, soirée… nombre et bornes libres). Une Availability référence un DayPart + une date.
  • Volunteer : email (identifiant fonctionnel unique par édition, nullable pour les bénévoles créés à la main), first_name, last_name, phone, tshirt_size, diet, comment. Pas de date de naissance.
  • Skill : référentiel par édition (SST, permis B, régie son, langues…). N-N avec Volunteer. N-N avec Shift (compétences requises).
  • Position : name, location, category, lead_account_id.
  • Shift : position_id, start_at, end_at (peut finir le lendemain), min_staff, ideal_staff, compétences requises (N-N Skill).
  • Assignment : volunteer_id, shift_id, status, source, locked.
  • Account : email, role, edition_id, volunteer_id (nullable).
  • MagicLinkToken : account_id, token_hash, expires_at, used_at.

Multi-édition : toute entité métier porte un edition_id.

Rôles et accès

  • COORDINATOR : accès complet, construit et publie les plannings.
  • LEAD (référent) : lecture sur toute l'édition, écriture limitée à ses propres postes.
  • VOLUNTEER : accès à ses seules données.

Authentification par lien magique envoyé par email, sans mot de passe. L'envoi d'email est stubé pour l'instant (le lien est écrit dans les logs). Un bénévole sans email n'a pas de Account et ne se connecte pas ; il est géré entièrement par la coordination.


3. Moteur de validation des affectations — cœur du produit

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. 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.
OUTSIDE_AVAILABILITY WARNING Un DayPart couvert par le créneau est UNAVAILABLE ou sans déclaration.
DAILY_HOURS_EXCEEDED WARNING > 8 h 00 d'affectations cumulées sur une même journée.
INSUFFICIENT_REST WARNING < 8 h 00 entre la fin d'un créneau et le début du suivant.
MISSING_REQUIRED_SKILL WARNING Le bénévole n'a pas une compétence requise par le créneau.

Sémantique figée :

  • Statuts pris en compte pour chevauchement / cumul / repos : PROPOSED + CONFIRMED. DECLINED et CANCELLED sont ignorés.
  • « Journée » (cumul 8 h) : du début du premier DayPart du jour au début du premier DayPart du lendemain.
  • Cumul : > 8 h 00 strict (8 h 00 pile passe).
  • Repos : < 8 h 00 strict.
  • Créneaux jointifs (end_at == start_at) : pas un chevauchement.
  • Une violation BLOCKING empêche l'enregistrement, sans dérogation.
  • Une violation WARNING est retournée à l'appelant mais n'empêche rien (l'équipe connaît des exceptions légitimes).

4. Périmètre

Premier incrément (en cours)

  1. Modèle de données complet + migrations Alembic.
  2. Import CSV/Excel (un seul fichier bénévoles + disponibilités), en deux temps : analyse (colonnes détectées, aperçu) puis exécution avec mapping validé. Rapport ligne par ligne. Détection des doublons : upsert sur email ; sans email, détection sur (last_name, first_name) signalée sans upsert automatique. Mode simulation qui ne persiste rien.
  3. API CRUD sur Position, Shift, Volunteer, Assignment.
  4. Moteur de validation (section 3).
  5. Endpoint matrice d'une édition : [{ position, shifts: [{ shift, min_staff, ideal_staff, confirmed, proposed }] }].
  6. Jeu de données de démonstration : 120 bénévoles, 15 postes, 4 jours, quelques trous d'effectif, 2–3 avertissements actifs.

Hors périmètre (le modèle doit juste pouvoir les accueillir sans refonte)

Solveur OR-Tools · envoi d'emails / SMS · constructeur de formulaire · pointage sur site · fonctions d'IA.


5. Arborescence

.
├── CLAUDE.md
├── README.md
├── docker-compose.yml              # dev : db + backend (hot reload)
├── .env.example
├── .github/workflows/deploy.yml    # CI/CD : build → GHCR → SSH sur le VPS
├── deploy/
│   ├── docker-compose.prod.yml
│   └── README.md                   # secrets requis, setup VPS
├── docker/postgres-init/           # création de la base de test au 1er boot
├── backend/
│   ├── pyproject.toml              # géré par uv
│   ├── Dockerfile
│   ├── alembic.ini
│   ├── app/
│   │   ├── main.py                 # instance FastAPI
│   │   ├── config.py               # settings (pydantic-settings)
│   │   ├── db.py                   # engine + session
│   │   ├── logging_config.py       # RGPD : pas de PII dans les logs
│   │   ├── api/                    # routers
│   │   ├── models/                 # tables SQLModel
│   │   ├── validation/             # moteur de règles (cœur du produit)
│   │   ├── i18n/
│   │   │   ├── __init__.py         # t(key, **params)
│   │   │   └── locales/fr.json
│   │   └── migrations/             # Alembic (env.py, versions/)
│   └── tests/
│       ├── conftest.py             # vraie base de test PostgreSQL
│       └── ...
└── frontend/                       # React + Vite + TS + Tailwind (étape ultérieure)

6. Commandes

Aucune dépendance locale requise hors Docker.

Action Commande
Lancer en dev docker compose up --build
API + docs interactives http://localhost:8000/docs
Tests backend docker compose run --rm backend uv run pytest
Couverture docker compose run --rm backend uv run pytest --cov=app
Lint docker compose run --rm backend uv run ruff check .
Format docker compose run --rm backend uv run ruff format .
Créer une migration docker compose run --rm backend uv run alembic revision --autogenerate -m "..."
Appliquer les migrations docker compose run --rm backend uv run alembic upgrade head
Charger le jeu de démo (étape ultérieure) docker compose run --rm backend uv run python -m app.seed

7. Conventions

  • Code, base, noms de fichiers en anglais. Le français vit dans les fichiers de traduction et les commits.
  • Aucun texte visible par l'utilisateur en dur. Backend : app.i18n.t(key)
    • locales/fr.json. Frontend : fichier i18n dédié. Vrai même en monolingue.
  • RGPD :
    • Pas de log des noms, emails, téléphones. On logue des identifiants.
    • Edition.retention_days : durée de rétention paramétrable.
    • Endpoint de suppression complète d'un bénévole : hard delete en cascade.
  • Dates et heures : stockées naïves, interprétées en heure locale Europe/Paris (heure murale). Les règles « 8 h », « journée », « demi-journée » sont toutes en heure locale.
  • Validation TDD : tests avant implémentation, un cas passant + un cas déclenchant par règle.
  • Git : un commit atomique par étape validée, message en français.
  • Étapes courtes : après chaque étape, montrer le résultat et attendre la validation avant de continuer.

8. Déploiement

  • Dépôt GitHub. VPS avec Docker.
  • .github/workflows/deploy.yml : sur chaque push main → build de l'image backend → push sur ghcr.io/<owner>/<repo>-backend → SSH sur le VPS → docker compose -f docker-compose.prod.yml pull && up -d.
  • Secrets requis (voir deploy/README.md) : SSH_HOST, SSH_USER, SSH_KEY, DEPLOY_PATH, GHCR_TOKEN.
  • Le VPS porte un fichier .env (non versionné) avec POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, JWT_SECRET.

9. Journal des étapes

Étape Description État
1 Squelette : monorepo, Docker, CI/CD, backend qui démarre à valider