Quatre corrections bloquantes/majeures de l'audit FIXME_M8 :
- Appariement un-à-un déterministe (consommation du candidat sélectionné) ;
1 réel / 2 théoriques → 1 REMOVED, 2 réels / 1 théorique → 1 ADDED.
- Filtrage strict par date : les cours réels hors target_date sont exclus
du matching avec un warning logé (décision architecte : pas d'exception).
- Déterminisme des détails : formatage via sorted(set(...)) au lieu de
set(...) brut, indépendant de PYTHONHASHSEED.
- Validateur AgendaChange strict : ADDED = lesson seule, REMOVED =
theoretical_lesson seule, MODIFIED = les deux requis.
- Comparaison à la minute près dans _is_modified (cohérent avec _matches).
- Documentation §8.4/§8.5 alignée avec l'implémentation (tolérance 15 min,
API compare(), normalize_subject référencé, appariement consommé).
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/tech-writer <tech-writer@agents.invalid>
Corrige les 5 constats de l'audit FIXME_M7 :
#1 (Bloquant) — Protection des événements non marqués :
- upsert_event() vérifie le marqueur X-PRONOTE-SYNC-MANAGED avant
modification ; lève PronoteSyncError en cas de collision avec un
événement non géré (aucune écriture)
- delete_event() vérifie le marqueur ; no-op avec warning si non géré
- Méthode privée _is_managed_event() factorisant le contrôle
#2 (Bloquant) — Fenêtre de synchronisation :
- Calcul en journées entières (minuit à minuit exclusif)
- Filtrage des données locales (lessons, homeworks, school_events) avant
passage au planner
- Paramètre now injectable pour les tests
#3 (Bloquant) — UID canonique vs brut :
- list_managed_events() retourne (raw_uid, canonical_uid, vevent)
- compute_plan() matche par UID canonique, route les raw UID vers
*_to_remove, retourne le mapping remote_raw_by_canonical
- executor.execute() utilise le raw UID pour les mises à jour (pas de
doublon)
- Pas de migration destructive des UID distants existants
#4 (Correction) — Normalisation temporelle UTC :
- normalize_datetime_to_utc() dans utils/uid.py : naïve → Europe/Paris →
UTC ; consciente → UTC
- Utilisée par generate_deterministic_uid() et component_to_signature()
- Deux représentations du même instant → même UID et même signature
#5 (Compatibilité) — date_search déprécié :
- Remplacement par calendar.search(start, end, event=True, expand=True)
Documentation :
- GUIDE_DEV_PYTHON.md : suppression des références obsolètes à
sync/state.py et état SQLite/JSON ; mise à jour de l'API CalDAV
(search au lieu de date_search, upsert par UID)
- TODO.md : M7 décoché (corrections en cours de validation)
Tests : 390 passés, couverture 95.61%
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
Co-authored-by: opencode/tech-writer <tech-writer@agents.invalid>
Met à jour la documentation et la configuration pour le jalon M7 selon
les décisions d'architecture :
- GUIDE_DEV_PYTHON.md §7 : API réelle caldav>=1.3.0 (pas le pseudo-code),
calendar_path (pas calendar_name), plan CalDAVSyncPlan explicite avant
exécution, pas d'état local (scan distant), événements non marqués
jamais modifiés
- TODO.md M7 : suppression de sync/state.py et BlogRSSState, ajout de
l'exécution du plan et de la protection des événements non gérés
- .pre-commit-config.yaml : caldav>=1.3.0 ajouté aux additional_dependencies
du hook mypy
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/tech-writer <tech-writer@agents.invalid>
Corrige les 5 points de l'audit FIXME_M6 :
1. Normalisation des matières : fonction normalize_subject (NFKC +
unification des espaces + suppression ponctuation + minuscule)
partagée par la génération d'ID et le futur comparateur M8.
2. Expurgation du secret dans l'erreur de collision d'IDs :
redact_secrets enveloppe l'identifiant dans le message.
3. Test even/odd avec même matière pour isoler la parité comme seul
différenciateur d'ID ; tests de normalisation (casse, espaces,
Unicode) ; test de non-fuite de secret.
4. TODO.md M6 : 8 items cochés après validation.
5. GUIDE_DEV §8.4 : bloc de code corrigé (clôture, types Lesson/
TheoreticalLesson, comparaison des horaires en minutes, début ET
fin, référence à normalize_subject).
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
Implémentation complète de la source d'agenda théorique :
- model.py : modèles Pydantic de parsing JSON (TheoreticalLessonEntry,
TheoreticalAgendaFile) avec validation des formats d'heure et de l'ordre
début/fin.
- parity.py : WeekParityService déterministe calculant la parité d'une
semaine (paire/impaire) à partir d'une date de référence.
- holidays.py : SchoolHolidayCalendar lisant un fichier JSON de vacances
scolaires (zone A) avec bornes inclusives.
- provider.py : protocole TheoreticalAgendaProvider (get_lessons,
get_lessons_for_range).
- file.py : JsonTheoreticalAgendaProvider implémentant le protocole :
filtrage par parité et vacances, génération d'IDs déterministes
incluant le type de semaine, validation de l'unicité des IDs,
tri stable par identifiant.
- __init__.py : factory get_theoretical_provider câblant la
configuration (None si désactivé, erreur si config de parité partielle).
- Fixtures : theoretical.json (9 leçons all/even/odd) et
school_holidays.json (zone A, 4 périodes).
- 57 tests unitaires couvrant parsing, parité, vacances, provider,
factory, déduplication de range, collisions d'IDs.
- Guide : §8 et §12 alignés avec le format JSON.
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
Met à jour la documentation et la configuration pour le jalon M6 selon
les décisions d'architecture :
- GUIDE_DEV_PYTHON.md §8 : remplace iCal/CSV par JSON avec parité de
semaine (paire/impaire) et calendrier de vacances scolaires séparé
- TODO.md M6 : nouveaux items (WeekParityService, SchoolHolidayCalendar,
configuration, fixtures JSON)
- .env.example : THEORETICAL_AGENDA_PATH passe en .json, ajout de
SCHOOL_HOLIDAYS_PATH, THEORETICAL_WEEK_ANCHOR_DATE et
THEORETICAL_WEEK_ANCHOR_TYPE
- AppSettings : 3 nouveaux champs (school_holidays_path,
theoretical_week_anchor_date, theoretical_week_anchor_type)
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/tech-writer <tech-writer@agents.invalid>
Corrige les 5 points de l'audit FIXME_M5 :
1. (Bloquant) Sépare transport HTTP et parsing : utilise requests.get()
avec timeout explicite et en-têtes conditionnels, puis transmet le
contenu à feedparser.parse() — supprime le paramètre inexistant
request_timeout qui faisait échouer toute récupération réelle.
2. Rejette les statuts HTTP 4xx/5xx via raise_for_status() avant le
parsing.
3. Préserve les validateurs de cache (etag, last_modified) d'entrée sur
les chemins d'échec (exception, bozo) au lieu de les écraser à None.
4. Sauvegarde atomique de BlogRSSState : écrit dans un .tmp puis
Path.replace() pour éviter la corruption sur interruption.
5. Déduplication normale silencieuse : les GUID déjà connus sont
ignorés sans warning ; seuls les doublons intra-flux génèrent un
avertissement.
Tests : 49 tests (32 client + 17 state) dont 11 nouveaux couvrant
transport HTTP réel, statuts 401/404/500, préservation des validateurs,
en-têtes conditionnels, doublons intra-flux et sauvegarde atomique.
Guide : §5 bis.7.1 aligné avec le nouveau pattern transport/parsing.
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
- Guide de développement (GUIDE_DEV_PYTHON.md) : spécification complète
- TODO.md : 15 jalons de développement (M1-M15) avec étapes et critères d'acceptation
- AGENTS.md : guide de contribution pour agents et développeurs
- pyproject.toml : configuration projet (dépendances, ruff, mypy strict, pytest)
- .gitignore : exclusion venv, bytecode, .env, caches, state files
- .env.example : template des variables d'environnement (placeholders)
- Structure du package pronote_sync/ (14 sous-packages avec __init__.py)
- tests/ avec conftest.py et fixtures/
- Environnement virtuel .venv/ (Python 3.14)