Files
college-infos/AGENTS.md
OpenCode 7dc48f6f43 docs: nettoyer AGENTS.md des règles redondantes
Retrait des sections de règles de développement de AGENTS.md, désormais centralisées dans orchestrator.md.

Co-Authored-By: Warp <agent@warp.dev>
2026-09-10 11:30:33 +02:00

10 KiB

Guide pour les agents et contributeurs — pronote-sync

Langue de travail : Français (ce fichier et toute la documentation projet).


1. Description du projet

pronote-sync est un pipeline Python qui synchronise les données de Pronote (système de gestion scolaire) vers CalDAV (agendas) et XMPP (notifications).

Spécification complète : Voir GUIDE_DEV_PYTHON.md.


2. Stack technique

Langage et dépendances

  • Python : ≥ 3.13.5 (actuellement 3.14 dans .venv/)
  • Dépendances principales : pydantic>=2.0, pydantic-settings, icalendar, caldav, slixmpp, pronotepy, feedparser, beautifulsoup4, requests, httpx, openai

Outils de développement

  • Linter : ruff (longueur de ligne : 100)
  • Typage : mypy (mode strict=true)
  • Tests : pytest (couverture ≥ 90%), pytest-mock, responses, aioresponses
  • Sécurité : bandit
  • Pre-commit : hooks configurés pour ruff, mypy, bandit

Environnement

  • Virtualenv : .venv/ (à activer avant toute opération)
  • Fichier de configuration : .env (à partir de .env.example)

3. Structure du projet

pronote_sync/
├── config/          # Configuration et paramètres
├── errors.py        # Hiérarchie canonique des erreurs du pipeline
├── models/          # Modèles de données (Pydantic v2)
├── sources/         # Connecteurs (Pronote, iCal, etc.)
├── sync/            # Logique de synchronisation
├── synthesis/       # Génération de synthèses (IA)
├── channels/        # Canaux de sortie (CalDAV, XMPP)
├── pipeline/        # Orchestration du pipeline
├── utils/           # Utilitaires (redaction, logging, etc.)
├── cli/             # Interface en ligne de commande
└── __init__.py

tests/
├── fixtures/        # Données de test anonymisées
├── unit/            # Tests unitaires
└── integration/     # Tests d'intégration

Plan de développement : Voir TODO.md pour les 15 jalons (M1-M15).


4. Commandes essentielles

Environnement

# Activer le virtualenv
source .venv/bin/activate

# Installer les dépendances de développement
pip install -e ".[dev]"

Qualité de code

# Linter (ruff)
ruff check .

# Formattage (ruff)
ruff format .

# Vérification des types (mypy)
mypy .

# Analyse de sécurité (bandit)
bandit -r pronote_sync/

# Pre-commit (tous les hooks)
pre-commit run --all-files

Tests

# Exécuter les tests
pytest

# Exécuter les tests avec couverture
pytest --cov

CLI

# Exécuter en mode dry-run (simulation)
pronote-sync --dry-run

5. Conventions de code

Typage

  • Strict : mypy en mode strict=true. Tous les paramètres, retours et variables locales doivent être typés.
  • Pydantic v2 : Tous les modèles de données héritent de BaseModel. Les modèles de contrat utilisent frozen=True.

Architecture

  • Injection de dépendances : Utiliser typing.Protocol et une composition root dans pipeline/run.py. Interdiction des singletons globaux.
  • Erreurs : Conserver une seule hiérarchie dans pronote_sync/errors.py ; ne pas créer de doublon dans pipeline/steps/errors.py.

Style

  • Longueur de ligne : 100 caractères maximum (configuré dans ruff).
  • Imports : Triés automatiquement par ruff (intègre isort).

Tests

  • Sans réseau : Utiliser uniquement des mocks (pytest-mock, responses, aioresponses).
  • Fixtures : Les données de test doivent être anonymisées et stockées dans tests/fixtures/.

Comportement

  • Idempotence : Deux exécutions identiques sans changement externe doivent produire le même résultat.
  • Mode dégradé :
    • Si l'IA échoue → retourner un message sans synthèse.
    • En mode source auto, essayer iCal puis utiliser pronotepy uniquement si iCal lève une exception.
    • En mode source explicite (ical ou pronotepy), ne pas changer silencieusement de source.
    • En mode auto, si iCal et pronotepy échouent → lever une erreur critique explicite.
    • Une liste vide est un succès valide ; elle ne doit pas être assimilée à une panne.

Contrat des sources Pronote

  • PRONOTE_URL (connexion API) et PRONOTE_ICAL_URL (flux iCal sensible) sont deux paramètres distincts ; ne pas déduire l'un de l'autre.
  • Le compte actuellement visé est un compte parent : utiliser pronotepy.ParentClient(pronote_url, username, password, ent=ent_function).
  • Résoudre le slug PRONOTE_ENT vers une fonction de pronotepy.ent au moyen d'une liste fermée.
  • Exposer séparément les cours et les devoirs dans le client ; filtrer les devoirs pronotepy sur la date cible.
  • Les récupérations critiques agenda/devoirs propagent une erreur expurgée ; seuls les messages et informations non critiques peuvent se dégrader en liste vide avec warning.
  • Réutiliser un téléchargement/parsing iCal pour l'agenda et les devoirs pendant un même run, sans cache global ni persistant.

Contrat d'authentification QR code / token

  • Le mode d'authentification est sélectionné par PRONOTE_AUTH_MODE :
    • password (défaut) : authentification classique via URL, identifiant, mot de passe et ENT.
    • qr_token : authentification par QR code puis token persistant (pour les instances Pronote utilisant HubEduConnect/EduConnect où l'authentification par mot de passe échoue).
  • En mode qr_token, le premier login utilise pronotepy.qrcode_login(qr_code, pin, uuid) avec les paramètres PRONOTE_QR_CODE_FILE (chemin du JSON QR) et PRONOTE_QR_PIN (PIN SecretStr).
  • Après chaque login réussi, les credentials exportées par pronotepy.export_credentials() sont persistées dans .pronote_auth_state.json (permissions 0600, format JSON versionné, écriture atomique). Le token rotate à chaque session — le fichier doit être mis à jour après chaque run.
  • Les logins suivants utilisent pronotepy.token_login(**credentials) avec le token persisté.
  • En cas d'échec de token_login (token expiré/invalide), une PronoteAuthRotationError est levée. Cette erreur se propage sans wrapping à travers PronoteFetcher et fetch_step jusqu'à PipelineRunner.run(), qui :
    • journalise l'erreur (expurgée) ;
    • envoie une notification XMPP actionnable si le canal est disponible et dry_run est inactif ;
    • retourne un résultat dégradé (None, errors).
  • PronoteAuthRotationError est re-levée telle quelle (except PronoteAuthRotationError: raise) dans toutes les couches d'enveloppement du chemin critique (fetch_agenda, fetch_homework, fetch_step). Ne pas l'attraper avec except Exception sans la re-léver d'abord.
  • Le fichier .pronote_auth_state.json ne doit jamais être committé (couvert par .gitignore). Son contenu (token vivant) ne doit jamais apparaître dans les logs, les messages d'erreur ou les notifications XMPP.

Contrat du provider openai-compatible

  • Le provider openai-compatible réutilise OpenAISynthesisProvider avec un base_url personnalisé ; aucun nouveau provider n'est créé.
  • AI_BASE_URL et AI_MODEL sont requis ; AI_API_KEY est requis (MVP).
  • L'URL doit utiliser https sauf si AI_ALLOW_INSECURE_HTTP=true.
  • Les credentials dans l'URL (user:pass@host) sont refusés.
  • Les paramètres sensibles dans la query string sont refusés, y compris ceux sans valeur (?token).
  • Les URL malformées ou sans hostname sont rejetées (ValueError catché).
  • Aucune manipulation automatique de /v1 n'est effectuée.
  • Configuration incomplète ou invalide → None avec avertissement (mode dégradé) ; la factory ne lève jamais d'exception.
  • La factory ne fait aucun appel réseau ; les avertissements utilisent redact_url().

Documentation (docstrings)

  • Obligatoire : Toute fonction, méthode et classe publique doit avoir une docstring.
  • Format : Utiliser le format Sphinx/reST (pas Google ou NumPy) pour une compatibilité native avec Sphinx.
  • Priorité : Les blocs historiques de GUIDE_DEV_PYTHON.md utilisant Args:/Returns: sont illustratifs ; le format Sphinx/reST défini ici prévaut pour le code de production.
  • Contenu :
    • Une ligne de résumé courte (une phrase).
    • Une description étendue optionnelle.
    • Les paramètres avec :param nom:.
    • Le retour avec :return: et :rtype:.
    • Les exceptions avec :raises TypeException:.
  • Modules : Chaque module doit avoir une docstring au niveau module.
  • Objectif : Générer une documentation PDF via LaTeX avec Sphinx.

Exemple :

def fetch_ical(url: str) -> str:
    """Récupère le contenu d'un flux iCal Pronote.

    :param url: URL du flux iCal (avec token ``icalsecurise``).
    :return: Contenu brut du flux iCal.
    :rtype: str
    :raises requests.RequestException: Si la requête HTTP échoue.
    """

6. Sécurité et secrets

Règles absolues

  • Aucun secret en clair : Ni dans le code, ni dans les logs, ni dans les erreurs, ni dans les fixtures.
  • Masquage : Utiliser systématiquement redact_url(), redact_secrets(), et redact_exception() depuis utils/redaction.py.
  • Chaînage d'exceptions : Ne jamais conserver comme __cause__ ou __context__ une exception externe brute susceptible de contenir un secret. Journaliser la version expurgée puis utiliser raise ... from None, ou chaîner une cause elle-même expurgée.
  • Tests de non-fuite : Vérifier les messages, les logs, __cause__, __context__ et le traceback complet avec des sentinelles distinctes pour chaque secret.

Bonnes pratiques

  • Types sécurisés : Les mots de passe et clés API doivent utiliser pydantic.SecretStr.
  • Fichier .env : Jamais committé (couvert par .gitignore). Utiliser .env.example comme template.

7. Plan de développement

Le projet suit un plan séquentiel en 15 jalons (M1-M15) décrits dans TODO.md.

  • Chaque jalon a des critères d'acceptation à valider avant de passer au suivant.
  • Ordre logique : scaffolding → config → modèles → sources → sync → synthèse → canaux → pipeline → CLI → tests → déploiement → docs

8. Référence

Document Rôle
GUIDE_DEV_PYTHON.md Spécification complète (architecture, modèles, parsing, déploiement)
TODO.md Plan de développement détaillé (15 jalons)