Aller au contenu

Z6 — Emploi du temps, Notifications & Révision proactive

ScheduleSlot · Notifications · Session evening_first · Session pre_class · Exam multi-chapitres · Mode dégradé

L'emploi du temps est le socle de toute la couche proactive. Une notification mal ciblée fatigue l'élève. Une session evening_first ou pre_class mal composée dilue la valeur du rappel. Un exam multi-chapitres mal borné explose le temps de session.

Z6-AC01 — Saisie de l'emploi du temps contextuelle au premier upload d'une matière

Lot 0
GIVEN L'élève uploade son premier chapitre d'une matière (ex : Physique-Chimie). Aucun ScheduleSlot n'existe pour cette matière.
WHEN Le formulaire de création de chapitre est validé (matière + nom + photos).
THEN Avant de lancer le pipeline J0, l'app affiche un écran inline : « Quand as-tu [Physique-Chimie] dans la semaine ? » avec une grille jour × période (Lu–Ve, matin/après-midi). L'élève coche les créneaux (ex : mardi matin + jeudi matin + vendredi après-midi). Un bouton « Plus tard » permet de sauter (l'app fonctionne sans, cf. Z6-AC11). À la confirmation, les ScheduleSlot correspondants sont créés en batch (un par case cochée). La contrainte d'unicité (user_id, subject, day_of_week, period) s'applique. Si l'élève uploade un 2ème chapitre de la même matière plus tard, cet écran n'apparaît pas (les ScheduleSlots existent déjà). La modification et la suppression restent possibles à tout moment dans les paramètres (CRUD standard).

NOTE : La saisie contextuelle au moment de l'upload résout le problème principal de l'onboarding : un élève de 12 ans ne connaît pas son emploi du temps par cœur et abandonne un formulaire abstrait. Mais au moment où il photographie son cours de Physique, il sait exactement quand il a cette matière — la question est naturelle, la réponse immédiate. L'emploi du temps se construit progressivement, matière par matière, au rythme des uploads. Après 2-3 captures (première semaine d'usage), l'agenda est complet sans que l'élève ait eu l'impression de « remplir un formulaire ».

Z6-AC02 — Notifications schedule-based (capture ou review)

Lot 0
GIVEN L'élève a un ScheduleSlot pour Physique-Chimie le mardi matin. C'est mardi, 18h (fenêtre de soirée).
WHEN Le service de notifications vérifie les ScheduleSlots du jour.
THEN Si la matière n'a aucun chapitre actif (pas encore capturé) : une notification capture_reminder est envoyée : « Tu as eu Physique-Chimie aujourd'hui — capture ton cours pour commencer à réviser ! » Le tap ouvre la caméra de capture. — Si la matière a au moins un chapitre actif avec des items en état FRAGILE, OK ou UNKNOWN : une notification review_reminder est envoyée : « C'est le moment de réviser Physique-Chimie — [N] points à revoir. » Le tap ouvre directement la session. La notification respecte la limite de Z6-AC03 (max 2/soir).

Z6-AC03 — Max 2 notifications par soir

Lot 0
GIVEN L'élève a 4 matières le mardi : Physique-Chimie, Maths, SVT, Français. 3 cours n'ont pas été saisis. 1 chapitre a des items FRAGILE.
WHEN Le scheduler prépare les notifications du mardi soir.
THEN Seules 2 notifications sont envoyées. La priorité est : (1) matière avec Exam le plus proche, (2) matière avec le plus d'items FRAGILE/UNKNOWN. Les 2 notifications restantes sont supprimées (pas reportées). L'élève ne reçoit jamais plus de 2 notifications par soir.

Z6-AC04 — Session evening_first déclenchée après upload

Lot 0 P1
GIVEN L'élève uploade un nouveau chapitre de Physique-Chimie. Le pipeline J0 produit 12 items.
WHEN Le pipeline J0 se termine avec ≥ 1 item valide.
THEN Une session de type evening_first est automatiquement proposée (pas lancée de force). trigger = 'scheduled'. Durée cible : 5–10 min. La session est proposée immédiatement, quelle que soit l'heure. Si l'élève ne la fait pas, elle reste disponible 72h (TTL session standard).

Z6-AC05 — evening_first : contenu 100% UNKNOWN, gabarits difficulté 1

Lot 0 P1
GIVEN Une session evening_first est composée pour un chapitre fraîchement uploadé avec 12 items UNKNOWN.
WHEN Le moteur de composition sélectionne les questions.
THEN 100% des items sont issus du chapitre uploadé. Tous sont en état UNKNOWN. Seuls les gabarits de difficulté 1 sont éligibles : GEN.KNOW.FLASH_MCQ, GEN.KNOW.DEF_SHORT, GEN.KNOW.CLOZE_KEYWORDS. Aucun gabarit NUMERIC, RUBRIC, ou de rédaction n'est inclus. Le nombre de questions est calibré pour 5–10 min (typiquement 6–10 questions).

Z6-AC06 — Session pre_class la veille de chaque cours

Lot 0
GIVEN L'élève a un ScheduleSlot mercredi matin pour Histoire-Géo. Il est mardi soir. L'élève a 3 chapitres actifs en Histoire-Géo avec des items aux états variés (UNKNOWN, FRAGILE, OK, SOLID).
WHEN Le scheduler de sessions évalue les sessions à proposer pour mardi soir.
THEN Une session pre_class est proposée. Durée cible : 5 min. La justification affichée est : 'Tu as Histoire-Géo demain — prépare-toi en cas d'interro surprise'. trigger = 'scheduled'.

Z6-AC07 — Fusion pre_class dans daily si session daily prévue le même soir

Lot 0
GIVEN L'élève a mardi soir : une session daily prévue (items dues de Maths + Physique) ET une session pre_class pour Histoire-Géo (cours mercredi matin).
WHEN Le moteur de composition prépare les sessions du mardi soir.
THEN Les items pre_class d'Histoire-Géo sont injectés en priorité dans la session daily. L'élève ne voit qu'une seule session. Les items pre_class apparaissent dans les premières questions. Le type de la session reste daily. L'attribut includes_pre_class = true est positionné pour le tracking.

Z6-AC08 — pre_class : scope multi-chapitres de la matière

Lot 0
GIVEN L'élève a 3 chapitres actifs en Histoire-Géo : 'Inégalités' (8 items, 3 FRAGILE), 'Mondialisation' (6 items, 1 UNKNOWN), 'Urbanisation' (10 items, 2 OK dues).
WHEN La session pre_class est composée.
THEN La sélection puise dans tous les chapitres actifs de la matière. Priorité : (1) items FRAGILE/OK dont next_due_at ≤ now, (2) items UNKNOWN jamais vus. Gabarits : difficulté 1–2 uniquement (rappel rapide, pas de problèmes longs). Durée cible : 5 min (typiquement 4–6 questions).

Z6-AC09 — Exam multi-chapitres : mock_exam couvre tous les chapitres liés

Lot 0
GIVEN Un Exam 'Contrôle séquence 1' avec chapter_ids = [ch1, ch2, ch3]. ch1 a 10 items, ch2 a 15 items, ch3 a 8 items.
WHEN L'élève lance un contrôle blanc (mock_exam) pour cet Exam.
THEN La session mock_exam inclut des items des 3 chapitres. La sélection est proportionnelle au nombre d'items par chapitre (≈ 30% ch1, 45% ch2, 25% ch3). Tous les niveaux de difficulté sont éligibles. La durée est cappée à 30 min maximum. Si le pool total dépasse 30 min, un échantillon représentatif est sélectionné.

Z6-AC10 — Exam multi-chapitres : création et liaison

Lot 0 P2
GIVEN L'élève crée un Exam 'Interro chapitre 3' avec exam_date = 2026-03-15 et sélectionne le chapitre 'Inégalités'.
WHEN L'Exam est sauvegardé.
THEN L'entité Exam est créée avec chapter_ids = ['ch_inegalites']. Le chapitre 'Inégalités' référence cet Exam. Le resserrement Z1-AC08 s'active pour tous les items du chapitre lié. Un Exam peut être modifié (ajout/retrait de chapitres, changement de date) à tout moment.

Z6-AC11 — Mode dégradé sans emploi du temps (global et par matière)

Lot 0 P1
GIVEN L'élève n'a pas (encore) renseigné son emploi du temps, ou l'a renseigné partiellement (certaines matières seulement).
WHEN L'élève utilise l'application.
THEN Mode global (aucun ScheduleSlot) : l'app fonctionne intégralement. Les sessions daily, evening_first et mock_exam restent disponibles. Seules les fonctionnalités dépendant de l'emploi du temps sont désactivées : pas de capture_reminder, pas de review_reminder, pas de session pre_class, pas de guidage « capture ton cours de demain ». Un bandeau non-bloquant rappelle périodiquement (max 1x/semaine) : « Ajoute ton emploi du temps pour recevoir des rappels personnalisés. » — Mode par matière (emploi du temps partiel) : chaque matière est indépendante. Les matières avec ScheduleSlots bénéficient des notifications proactives. Les matières sans ScheduleSlots fonctionnent en mode dégradé individuel. La carte de leçon d'une matière sans emploi du temps affiche un bandeau contextuel : « Ajoute les horaires de [matière] pour des rappels adaptés. » Le bandeau disparaît dès que les ScheduleSlots sont ajoutés.

Z6-AC12 — Pas de session pre_class si aucun chapitre actif dans la matière

Lot 0
GIVEN L'élève a un ScheduleSlot mercredi matin pour SVT. Aucun chapitre SVT n'a été créé (ou tous sont archivés).
WHEN Le scheduler évalue les sessions pre_class pour mardi soir.
THEN Aucune session pre_class n'est créée pour SVT. Aucune notification pre_class n'est envoyée. Le système n'affiche pas d'erreur.

Z6-AC13 — Items dues non révisés : aucune pénalité mastery

Lot 0 P1
GIVEN Un item en état OK avec next_due_at = 2 mars. L'élève ne révise pas du 2 au 5 mars (3 jours de retard).
WHEN L'élève ouvre une session le 5 mars.
THEN L'item est toujours en état OK. consecutive_successes n'a pas changé. next_due_at est resté au 2 mars (non modifié par l'inaction). L'item apparaît en priorité dans les 70% « items dus » de la session (car next_due_at < now). Aucune régression n'a eu lieu.

NOTE : Le Mastery state n'est JAMAIS modifié par l'inaction. Seule une réponse incorrecte déclenche une régression (Z1-AC05 à Z1-AC07). Un élève qui revient après une pause retrouve ses acquis intacts et reprend là où il en était. Cette règle s'applique à tous les états (FRAGILE, OK, SOLID).

Z6-AC14 — Rappel unique le lendemain pour session manquée

Lot 0
GIVEN L'élève a manqué une session hier soir (evening_first ou pre_class non complétée avant la fin de la fenêtre de soirée).
WHEN Le lendemain matin (8h), le service de notifications vérifie les sessions non complétées.
THEN Un rappel unique est envoyé : — evening_first manquée : « Tu n'as pas eu le temps de découvrir [chapitre] hier — essaie ce soir ! » — pre_class manquée : « Tu n'as pas révisé [matière] hier soir — tu as cours aujourd'hui, essaie ce midi ou ce soir. » Le rappel est envoyé une seule fois (pas de relance les jours suivants). Les sessions daily manquées ne déclenchent aucun rappel (cf. Z6-AC13 : pas de pénalité, pas de culpabilisation). Le rappel respecte la limite globale de notifications (Z6-AC03).

Z6-AC15 — Aucune mécanique de streak

Lot 0
GIVEN L'élève a révisé 5 jours consécutifs puis ne révise pas pendant 3 jours.
WHEN L'élève revient le 9e jour et ouvre l'application.
THEN Aucun compteur de « série » ou de « streak » n'est affiché. Aucun message de type « Tu as perdu ta série » n'apparaît. L'interface affiche l'état actuel de la carte de maîtrise sans référence à la régularité passée. Le message d'accueil est neutre ou positif : « Tes révisions t'attendent — on continue ? ».

NOTE : L'absence de streak est un choix produit délibéré. La gamification par streak culpabilise les élèves en cas de rupture et peut être contre-productive pour les collégiens (11–15 ans). Le service valorise la qualité de la révision, pas la quantité de jours consécutifs.

Z6-AC16 — Exceptions d'emploi du temps (annulation, déplacement)

Lot 0
GIVEN L'élève a un ScheduleSlot récurrent pour Physique-Chimie le mardi matin. Un événement exceptionnel survient cette semaine.
WHEN L'élève crée une ScheduleException via l'interface.
THEN Annulation (type = 'cancelled') : les notifications capture_reminder et review_reminder de ce créneau sont supprimées pour cette semaine. La session pre_class de la veille est supprimée. Les sessions daily ne sont pas affectées. — Déplacement (type = 'moved', target_day spécifié) : les notifications et la session pre_class sont recalculées pour le nouveau jour. Si le jour cible a déjà un créneau pour la même matière, les sessions ne sont pas dupliquées (déduplication). — Isolation par matière : une exception sur Physique-Chimie n'affecte pas les créneaux de Histoire-Géo ou d'autres matières. Les exceptions s'appliquent uniquement à la matière et au créneau spécifiés. Les exceptions passées (> 30 jours) sont nettoyées automatiquement.

Z6-AC17 — Notification parent : annulation / déplacement de cours (temps réel)

Lot 0
GIVEN L'élève a un parent lié (linked_student_id). Le parent a schedule_change_enabled = true dans ses ParentNotificationPref. L'élève annule son cours de PC du mardi 11 mars.
WHEN L'élève confirme l'annulation.
THEN Une notification push est envoyée immédiatement au parent : « [Prénom] a annulé son cours de Physique-Chimie du mardi 11 mars ». L'événement apparaît dans le tableau de bord parent (section « 7 derniers jours »). L'élève n'est pas notifié que son parent a reçu l'alerte. Si le parent a schedule_change_enabled = false, aucune notification n'est envoyée mais l'événement reste visible dans le tableau de bord.

Z6-AC18 — Notification parent : session de révision manquée (lendemain matin)

Lot 0
GIVEN L'élève avait une session pre_class planifiée le lundi 10 mars soir pour PC. La session n'a pas été commencée à 23h59. Le parent a missed_session_enabled = true.
WHEN Le scheduler parent s'exécute le mardi 11 mars matin (même heure que le rappel élève).
THEN Notification push parent : « [Prénom] n'a pas fait sa session de révision de Physique-Chimie hier soir ». L'événement apparaît dans le tableau de bord parent. Exception : si l'élève a terminé la session entre 00h00 et 06h00 le mardi, la notification parent n'est pas envoyée (session comptée comme faite en retard).

Z6-AC19 — Notification parent : inactivité prolongée (3 jours)

Lot 0
GIVEN L'élève n'a eu aucune activité (capture, session, review) depuis 3 jours consécutifs. Le parent a inactivity_enabled = true et inactivity_threshold_days = 3.
WHEN Le job d'inactivité s'exécute le matin du 4ème jour sans activité.
THEN Notification push parent : « [Prénom] n'a pas utilisé ReviseMieux depuis 3 jours ». L'alerte est envoyée une seule fois par période d'inactivité. Aucune nouvelle notification tant que l'élève n'a pas repris une activité puis recommencé une nouvelle période d'inactivité. Le seuil est configurable par le parent (inactivity_threshold_days).

Z6-AC20 — Opt-out parent par catégorie

Lot 0
GIVEN Le parent désactive missed_session_enabled dans ses paramètres mais laisse schedule_change_enabled et inactivity_enabled activés.
WHEN L'élève manque une session ET annule un cours le même jour.
THEN Le parent reçoit uniquement la notification d'annulation de cours (schedule_change). Aucune notification pour la session manquée. Les deux événements restent visibles dans le tableau de bord parent (le tableau de bord n'est pas filtré par les préférences de notification).

NOTE : Les notifications parent respectent l'autonomie de l'élève. L'objectif est d'informer les parents sans créer une dynamique de surveillance. L'élève ne voit jamais « ton parent a été prévenu ». Le parent ne peut pas agir sur l'emploi du temps de l'élève depuis son compte.

Z6-AC21 — Ré-engagement progressif après inactivité prolongée (7+ jours)

Lot 0
GIVEN L'élève n'a eu aucune activité depuis 7 jours consécutifs. Le rappel d'inactivité parent (Z6-AC19) a déjà été envoyé à J+3.
WHEN Le job de ré-engagement s'exécute le matin du 8ème jour.
THEN Une notification unique est envoyée à l'élève : « Tes révisions t'attendent — [N] points à consolider en [matière]. On reprend doucement ? ». La notification inclut un deep link vers une session courte (3–5 min, gabarits faciles, priorité items FRAGILE). Si l'élève ne réagit pas, aucune relance supplémentaire n'est envoyée (respect du choix). Le système ne relance qu'au prochain changement de contexte : nouvel Exam créé, nouveau chapitre uploadé, ou début de trimestre.

NOTE : Ce AC complète Z6-AC19 (alerte parent) et Z6-AC15 (pas de streak) en ajoutant un seul point de contact côté élève. L'approche "1 notification + deep link facile" respecte le principe anti-culpabilisation tout en offrant un chemin de retour à faible friction. Le déclencheur contextuel (exam, upload, trimestre) évite le harcèlement tout en maintenant des occasions naturelles de reprise.

Z6-AC22 — Session « retour en douceur » après absence prolongée

Lot 0
GIVEN L'élève n'a eu aucune activité depuis ≥ 5 jours. Il a 15 items en retard (next_due_at < now), dont 8 FRAGILE et 7 OK. Il ouvre l'app et lance une session.
WHEN Le moteur de composition prépare la session.
THEN La session est composée en mode « retour en douceur » : — Durée réduite : 5 min max (au lieu de 10–20 min). — Sélection : uniquement les 4–6 items les plus anciens en retard (pas les 15). — Gabarits : difficulté 1–2 uniquement (rappel, pas d'exercice long). — Message d'accueil : « Content de te revoir ! On reprend doucement avec quelques rappels. ». — Les items non sélectionnés restent en retard et seront proposés dans les sessions suivantes (étalement sur 3–5 jours). Le mode « retour en douceur » se désactive automatiquement après 2 sessions complétées consécutivement.

NOTE : Sans ce mécanisme, un élève qui revient après une semaine voit une session de 20 min bourrée d'items qu'il a oubliés → cascade d'échecs → sentiment d'incompétence → décrochage définitif. Le « retour en douceur » étale la dette sur plusieurs jours et utilise des gabarits faciles pour reconstruire la confiance avant de monter en difficulté. C'est le premier anti-pattern de churn identifié dans les apps de spaced repetition (cf. problème connu d'Anki).

Z6-AC23 — Cycle de vie post-exam : archivage automatique

Lot 0
GIVEN Un Exam avec exam_date = 10 mars couvre les chapitres ch1 et ch2. La date est dépassée (now > exam_date + 1 jour).
WHEN Le job quotidien de maintenance s'exécute le 12 mars.
THEN L'Exam passe en status = 'past'. Les chapitres ch1 et ch2 ne sont plus soumis au resserrement de planning Z1-AC08 (les intervalles reviennent aux valeurs standard sans exam). Les items SOLID de ch1/ch2 passent en intervalle J+14 (repos long terme au lieu de J+7). Les items FRAGILE/OK gardent leurs intervalles standard (J+1, J+3). Les chapitres restent actifs et révisables mais ne sont plus prioritaires dans la session quotidienne. Un message « Contrôle passé — tes acquis sont en maintenance longue » est affiché sur la carte du chapitre. L'élève peut relancer un contrôle blanc à tout moment (utile pour un futur brevet ou examen global).

NOTE : Sans ce AC, les items post-exam continuent de saturer les sessions quotidiennes avec le même rythme qu'avant l'exam. L'élève a mentalement tourné la page mais l'app insiste. C'est la 3ème cause de désinstallation identifiée. Le passage en « maintenance longue » (J+14 pour SOLID) maintient l'ancrage sans fatiguer.

Z6-AC24 — Diagnostic initial : rampe de difficulté progressive

Lot 0
GIVEN L'élève lance son premier diagnostic sur un chapitre avec 12 items UNKNOWN.
WHEN Le moteur de composition prépare le diagnostic initial.
THEN Les 2–3 premières questions sont des gabarits de difficulté 1 (FLASH_MCQ, DEF_SHORT) sur les items les plus simples (confidence la plus haute). Les questions suivantes montent progressivement en difficulté (2, puis 3 si disponible). Si l'élève enchaîne 3 échecs consécutifs, le moteur redescend en difficulté 1 pour les 2 questions suivantes avant de remonter. Le diagnostic ne commence jamais par un gabarit NUMERIC, RUBRIC ou ORDERING. En fin de diagnostic, le message de clôture est toujours positif : « Bonne première exploration ! Tu as [X] points acquis et [Y] à travailler — on s'y met dès ce soir ! ».

NOTE : La première impression détermine la rétention. Un diagnostic qui commence par un exercice de calcul complexe sur un chapitre jamais vu → échec → l'élève pense « cette app est trop dure ». La rampe progressive garantit 2–3 réussites rapides en début de session (effet psychologique de compétence perçue) avant d'augmenter le challenge.

Z6-AC25 — Notification parent : digest hebdo calé sur calendrier d'exams

Lot 0
GIVEN Un Exam est prévu le mercredi 12 mars. Le digest hebdo parent est normalement envoyé le dimanche.
WHEN Le scheduler prépare le digest de la semaine contenant un exam à J-5 ou moins.
THEN Un digest supplémentaire « pré-contrôle » est envoyé 3 jours avant l'exam (samedi 9 mars) en plus du digest hebdo standard. Ce digest inclut : — Titre : « Contrôle [Matière] dans 3 jours ». — Maîtrise par chapitre concerné (% items OK+SOLID). Si l'exam a notion_ids[] (Z7-AC19), le calcul de maîtrise et la liste d'items ne portent que sur les Notions sélectionnées (pas le chapitre entier). — Items encore FRAGILE/UNKNOWN dans le périmètre de l'exam (liste courte, max 5). — Recommandation : « Encouragez [Prénom] à faire un dernier contrôle blanc ce week-end ». Le digest standard du dimanche inclut une section « Contrôle dans 3 jours » en haut si non envoyé samedi. Aucun digest supplémentaire si le parent a opt-out de la catégorie.

NOTE : Le digest hebdo à date fixe ne suffit pas : un parent qui reçoit le récap dimanche pour un contrôle lundi n'a plus le temps d'agir. Le digest pré-contrôle à J-3 donne une fenêtre d'action (week-end). C'est le moment où le parent a le plus besoin du signal et où la valeur perçue du service est la plus haute.

Z6-AC26 — Notification parent : résumé hebdo sessions manquées (anti alert-fatigue)

Lot 0
GIVEN L'élève a manqué 3 sessions cette semaine (lundi pre_class, mercredi evening_first, vendredi daily). Le parent a missed_session_enabled = true.
WHEN Le scheduler parent prépare les notifications de session manquée.
THEN Le parent ne reçoit PAS 3 notifications individuelles. À la place : — Première session manquée de la semaine : notification push individuelle (cf. Z6-AC18). — Sessions manquées suivantes (2ème et 3ème) : regroupées dans le digest hebdo sous la section « Sessions manquées cette semaine : 3 ». Pas de notification push supplémentaire. Maximum 1 notification push « session manquée » par semaine pour le parent. Le détail complet reste visible dans le tableau de bord parent (Z6-AC20).

NOTE : Un parent qui reçoit 3+ notifications « session manquée » par semaine désactive les alertes. L'alert fatigue est la première cause de désactivation des notifications parent dans les apps éducatives. La règle « 1 push/semaine + résumé dans le digest » maintient le signal sans créer de bruit. Le tableau de bord reste exhaustif pour les parents qui veulent le détail.

Z6-AC27 — Digest parent hebdo : contenu standardisé avec indicateur qualité

Lot 0
GIVEN Le parent est en mode passif. Le scheduler prépare le digest hebdomadaire. L'élève a 2 chapitres actifs : 'Photosynthèse' (15 items, 3 validation_required, 80% OK+SOLID) et 'Densité' (10 items, 0 validation_required, 60% OK+SOLID).
WHEN Le digest est généré.
THEN Le digest contient obligatoirement les sections suivantes, dans cet ordre : — 1. Résumé activité : « [Prénom] a révisé [X] fois cette semaine, [Y] min au total ([Y₁] min en session + [Y₂] min en étude papier) ». Si des fiches papier ont été reportées (Z7-AC26, session_type = 'paper_report'), le temps en étude papier (estimé : nb questions × 90s) est affiché séparément du temps en session interactive. Si aucun report papier : le détail n'est pas affiché (juste « [Y] min au total »). Si aucune activité : « [Prénom] n'a pas révisé cette semaine » (pas de données masquées). — 2. Maîtrise par chapitre : pour chaque chapitre actif, le % d'items OK+SOLID et le nombre d'items restants (FRAGILE+UNKNOWN). Si des items ont validation_required = true, mention « [N] point(s) en vérification — exercices simplifiés en attendant ». — 3. Alertes (si applicable) : items à risque (FRAGILE + prochain exam < 5j), sessions manquées (résumé, cf. Z6-AC26), inactivité. — 4. Prochaine action : « Encouragez [Prénom] à [action concrète] ». Ex. « faire le contrôle blanc de Physique-Chimie ce week-end ». — 5. Score dernier contrôle blanc (si complété cette semaine) : score + score_confidence (cf. Z1-AC18). Le digest est lisible en < 30 secondes (max 150 mots hors titres).

NOTE : Le digest est le touchpoint principal des parents passifs (mode par défaut). Son contenu était sous-spécifié — un vague « couverture, maîtrise, risques » sans format. Ce AC standardise les 5 sections obligatoires et surtout rend visible le statut de qualité des items (section 2). Un parent qui voit « 3 points en vérification » comprend que la maîtrise affichée est provisoire, ce qui évite la fausse confiance.

Z6-AC28 — Digest parent : signalement capture incomplète (pages OCR échouées)

Lot 0
GIVEN L'élève a uploadé 10 pages pour le chapitre 'Photosynthèse'. 2 pages sont en status = FAILED ou items_generation_failed. Le parent reçoit le digest hebdomadaire.
WHEN Le digest est généré pour ce chapitre.
THEN La section maîtrise du chapitre inclut une mention : « ⚠ 2 pages sur 10 n'ont pas pu être analysées — le cours est partiellement couvert. [Prénom] peut reprendre les photos pour compléter. ». Si le nombre de pages échouées représente > 30% du total, la mention est promue en alerte (section 3) : « Attention : plus de 30% du cours de [Matière] n'a pas été analysé. Les exercices et le contrôle blanc ne couvrent pas tout le programme. ». L'alerte est envoyée une seule fois (pas répétée chaque semaine si l'élève ne corrige pas).

NOTE : C'est un gap critique identifié : le parent ne savait pas que la capture était incomplète. Il voyait « 80% de maîtrise sur Photosynthèse » sans savoir que 20% du contenu manquait. Le contrôle blanc sur contenu incomplet donne un score trompeur. Ce AC ferme la boucle entre « problème pipeline » et « parent informé ».

Z6-AC29 — Labels de maîtrise traduits pour le parent

Lot 0
GIVEN Le digest ou le tableau de bord parent affiche les états de maîtrise des items.
WHEN Le parent consulte les données de maîtrise.
THEN Les labels techniques sont traduits en langage parent : — UNKNOWN → « Pas encore vu ». — FRAGILE → « En cours d'apprentissage ». — OK → « Compris, à consolider ». — SOLID → « Bien acquis ». — Un item avec validation_required = true affiche « En vérification » à la place de son état mastery. Le tooltip (ou sous-texte au premier affichage) explique brièvement ce que signifie chaque niveau. Le % de maîtrise du digest est calculé sur les items OK + SOLID uniquement (FRAGILE et UNKNOWN ne comptent pas comme « maîtrisés »).

NOTE : Les labels internes (UNKNOWN, FRAGILE, OK, SOLID) sont du jargon développeur. Un parent qui voit « 3 items FRAGILE » peut paniquer (« fragile = mauvais ») alors que ça signifie « en cours d'apprentissage, normal après 1 session ». La traduction en langage naturel et l'explication au premier affichage éliminent cette source de confusion.

Z6-AC30 — Vue progression globale cross-chapitres

Lot 0
GIVEN L'élève a 3 chapitres actifs (HG-INEG avec 12 items, HG-FEOD avec 8 items, PC-TRANSF avec 15 items). Les états de maîtrise sont variés (mix UNKNOWN/FRAGILE/OK/SOLID).
WHEN L'élève accède à son tableau de bord principal.
THEN Un indicateur de progression globale est affiché : % maîtrise global = nombre d'items OK+SOLID / nombre total d'items actifs (non archivés). Chaque chapitre est listé avec sa propre barre de progression visuelle (proportionnelle au nombre d'items). Les chapitres avec un exam à venir dans les 7 jours sont marqués visuellement (badge ou couleur). Un message d'encouragement contextuel est affiché basé sur la tendance (ex: « +12% cette semaine, continue ! » ou « Tu reprends bien après ta pause »). Les items avec validation_required = true sont comptés dans le total mais marqués visuellement comme « en vérification ».

NOTE : Un élève de 13 ans a besoin de voir sa progression globale, pas juste chapitre par chapitre. Sans cet indicateur, l'élève qui a 3 chapitres en cours ne perçoit pas qu'il progresse (« j'ai SOLID sur 2 trucs en HG mais je sais pas où j'en suis au total »). La barre de progression visuelle + le message contextuel exploitent le biais d'engagement de la progression : un % qui monte motive à continuer.

Z6-AC31 — Résilience réseau : persistance optimiste des réponses en session

Lot 0
GIVEN L'élève est en session et répond à une question. La connexion réseau est instable ou momentanément perdue.
WHEN La réponse est soumise par le client.
THEN La réponse est immédiatement persistée localement (storage client) avec un statut pending_sync. Le feedback de correction est affiché instantanément (calcul client pour MCQ/NUMERIC, grading local basé sur expected_answer). La session continue sans attendre la confirmation serveur. Un indicateur discret « synchronisation en cours… » est visible si la connexion est perdue. Dès que la connexion revient, les Attempts pending_sync sont envoyés au serveur en FIFO. En cas de conflit (Attempt déjà existant côté serveur pour la même question), le client gagne (last-write-wins sur le même question_id + user_id). Si la synchronisation échoue après 3 retries espacés (5s, 15s, 45s), l'Attempt reste pending_sync et un message s'affiche : « Certaines réponses n'ont pas pu être enregistrées. Elles seront synchronisées à la prochaine connexion. » Les Mastery updates côté serveur sont appliqués uniquement à la réception des Attempts synchronisés (pas de mise à jour optimiste du mastery, seul le feedback est optimiste).

NOTE : Un collégien utilise l'app en transport en commun, dans sa chambre avec du wifi instable, ou en zone blanche. Sans persistance optimiste, une déconnexion de 10 secondes = réponse perdue + l'élève doit recommencer = frustration maximale → fermeture de l'app. Le feedback optimiste local permet une UX fluide. Le mastery serveur reste cohérent car il n'est mis à jour qu'à la synchro confirmée.

Z6-AC32 — Support multi-exam par chapitre et resserrement sur l'exam le plus proche

Lot 0
GIVEN Le chapitre « Les inégalités » est lié à deux exams : une interrogation chapitre le 15 mars (exam_interro) et un contrôle de séquence le 22 mars (exam_sequence). Le modèle utilise Chapter.exam_ids[] (pluriel). Le resserrement Z1-AC08 doit choisir une date de référence.
WHEN Le moteur de composition calcule les intervalles pour les items de ce chapitre.
THEN Le resserrement utilise l'exam actif (status = active) le plus proche comme référence pour T = exam_date - now. Si exam_interro est le 15 mars et exam_sequence le 22 mars, T est calculé sur le 15 mars. Après le 15 mars, exam_interro passe en status = past (Z6-AC23), et le resserrement bascule automatiquement sur exam_sequence (T recalculé sur le 22 mars). Le digest parent pré-contrôle (Z6-AC25) est envoyé à J-3 de chaque exam actif (deux digests distincts si les dates sont espacées de > 3 jours). Le mock exam (Z6-AC09) est composé pour l'exam le plus proche. Si l'élève supprime un exam, les next_due_at sont recalculés sur l'exam actif suivant, ou décompressés si aucun exam actif ne reste (cohérent avec Z4-AC17).

NOTE : Le modèle initial avait Chapter.exam_id (singulier) — un chapitre ne pouvait référencer qu'un seul exam. Or au collège, un chapitre peut être interrogé en interro de chapitre PUIS en contrôle de séquence/brevet blanc. Sans le pluriel, l'élève devait choisir quel exam lier, et le resserrement ne fonctionnait que pour un seul. La bascule automatique entre exams évite une rupture de révision entre l'interro et le contrôle de séquence.

Z6-AC33 — Session evening_first incrémentale après ajout de pages

Lot 0
GIVEN Le chapitre « Mouvement et vitesse » existe depuis lundi avec 8 items (4 FRAGILE, 4 OK). Mercredi soir, l'élève ajoute les pages 4-6 (Z5-AC11). Le pipeline incrémental (Z2-AC14) génère 6 nouveaux items en état UNKNOWN. Le chapitre a maintenant 14 items au total (4 FRAGILE + 4 OK + 6 UNKNOWN).
WHEN Le pipeline incrémental se termine avec ≥ 1 nouvel item valide.
THEN Une session de type evening_first_incremental est proposée (pas lancée de force). La session contient uniquement les nouveaux items UNKNOWN issus des pages ajoutées (pas les items FRAGILE/OK existants — ceux-ci seront couverts par la session daily normale). Les gabarits sont limités à la difficulté 1 (identique à Z6-AC05 : GEN.KNOW.FLASH_MCQ, GEN.KNOW.DEF_SHORT, GEN.KNOW.CLOZE_KEYWORDS). Durée cible : proportionnelle au nombre de nouveaux items (3-5 min pour 6 items). Si l'élève a déjà une session daily composée pour la soirée, la session evening_first_incremental est fusionnée dans la daily : les nouveaux items UNKNOWN sont insérés dans le bucket 20% (nouveaux) de la politique 70/20/10 (Z6-AC07 étendu). Le message de notification est contextualisé : « 6 nouveaux points ajoutés à Mouvement et vitesse — révise-les ce soir ! » (et non « Nouveau chapitre uploadé »).

NOTE : La session evening_first standard (Z6-AC04/05) est conçue pour un chapitre fraîchement uploadé où tous les items sont UNKNOWN. Dans le cas incrémental, proposer une evening_first « classique » incluant les items existants FRAGILE/OK briserait la logique 70/20/10 et re-proposerait des items déjà vus avec des gabarits trop simples. La session incrémentale cible uniquement le contenu frais pour le premier contact, puis laisse la session daily intégrer les nouveaux items dans le flux normal dès le lendemain.

Z6-AC34 — Dashboard : explication de dilution de maîtrise après ajout de pages

Lot 0
GIVEN Le chapitre « Mouvement et vitesse » avait 8 items (4 OK + 4 SOLID = 100% maîtrise affichée). L'élève ajoute les pages 4-6 et le pipeline génère 6 nouveaux items UNKNOWN. La maîtrise affichée passe à 8/14 = 57%.
WHEN L'élève consulte son dashboard ou la vue chapitre après l'ajout de pages.
THEN Un message contextuel s'affiche au-dessus de la barre de progression du chapitre : « +6 nouveaux points ajoutés — ta maîtrise va remonter au fil des révisions. » Le message est accompagné d'une micro-animation (apparition des 6 nouveaux points en état UNKNOWN dans la barre de progression, visuellement distincts des points existants). Le message disparaît après 72h ou après que l'élève a répondu à au moins une question sur un des nouveaux items (premier contact effectué). La vue progression globale (Z6-AC30) affiche une annotation similaire au niveau matière si la maîtrise globale a baissé de > 10 points de pourcentage suite à l'ajout. Le digest parent (Z6-AC27) mentionne l'ajout de pages dans la section « Activité de la semaine » : « [N] nouveaux points ajoutés à [chapitre] le [date]. »

NOTE : Sans cette explication, la baisse brutale de pourcentage est anxiogène pour l'élève (« j'ai régressé ? ») et le parent (« il ne révise plus ? »). La dilution de maîtrise est un artefact arithmétique normal — de nouveaux points UNKNOWN font baisser la moyenne — mais elle ressemble visuellement à une régression. Le message contextuel transforme une surprise négative en confirmation positive (« tu as ajouté du contenu, bravo »).

Z6-AC35 — Onboarding parent : création de compte et liaison à l'élève

Lot 0
GIVEN L'élève a créé son compte et renseigné son emploi du temps. Il souhaite inviter un parent.
WHEN L'élève tape sur « Inviter un parent » (accessible dans les paramètres ou proposé en fin d'onboarding, cf. Z8-AC07 pour le timing optimal).
THEN Le mécanisme de liaison par code 6 caractères défini dans Z6-AC44 est déclenché (génération code, saisie parent, confirmation élève). À la fin de la liaison, le parent accède immédiatement au dashboard parent simplifié (digest Z6-AC27, progression globale, script 3 minutes Z1-AC17). La liaison est unidirectionnelle : le parent voit les données de l'élève, l'élève ne voit pas le compte parent. Si aucun parent n'est lié, les fonctionnalités parent (digests, notifications) sont inactives — pas d'erreur, simplement pas de destinataire. L'onboarding élève mentionne l'invitation parent mais ne la rend pas obligatoire : « Invite un parent pour qu'il suive tes progrès — tu peux le faire plus tard. »

NOTE : Cet AC couvre le parcours utilisateur de l'onboarding parent (quand et comment l'invitation est proposée). Le mécanisme technique de liaison (code, validation, entités) est défini dans Z6-AC44. Le timing optimal de la première invitation est spécifié dans Z8-AC07 (après la 1re session).

Z6-AC36 — Comportement le jour de l'examen

Lot 0
GIVEN Un Exam « Contrôle Physique » a exam_date = 2026-03-15. Nous sommes le 15 mars. L'Exam couvre 2 chapitres (14 items).
WHEN L'élève ouvre l'app le jour de l'examen.
THEN (1) Le matin (avant 14h) : le dashboard affiche un message d'encouragement : « Contrôle de Physique aujourd'hui — tu es prêt ! Bonne chance ! 🍀 » Aucune session de révision n'est proposée pour les items de cet Exam (trop tard pour la révision espacée, risque d'anxiété). Les items des chapitres liés à l'Exam sont temporairement exclus du daily. Un lien optionnel « Relire ma leçon » ouvre la carte de leçon en mode lecture seule (pas d'exercices). (2) Le soir : l'EveningPlan exclut les items de l'Exam (pas de révision le soir même du contrôle — l'effort cognitif post-exam est contre-productif). Les autres matières sont proposées normalement. (3) Le lendemain (J+1) : l'Exam passe en status = past. Les items liés passent en mode maintenance longue : next_due_at recalculé à J+14 pour les SOLID, J+7 pour les OK, J+3 pour les FRAGILE (au lieu des intervalles compressés Z1-AC08). Le message dans le dashboard : « Contrôle de Physique passé ! Les révisions de ce chapitre sont allégées. » (4) Notification parent le jour J : « [Prénom] a son contrôle de Physique aujourd'hui. » (si parent_schedule_change_enabled = true).

NOTE : Le jour de l'exam est un non-lieu pédagogique pour la révision espacée : réviser le matin même n'améliore pas significativement la rétention (l'encodage est déjà consolidé ou pas) et génère de l'anxiété. L'app doit passer du mode « coach de révision » au mode « supporteur » le jour J. L'exclusion des items exam du daily évite le cas absurde où l'app propose « Révise la masse volumique ce soir » alors que l'élève vient de passer le contrôle dessus.

Z6-AC37 — Multi-exam overlapping : reset + recompression entre exams

Lot 0
GIVEN L'élève a Exam1 « Interro Densité » (15 mars, chapitre Densité) et Exam2 « Contrôle séquence » (22 mars, chapitres Densité + Forces). Nous sommes le 16 mars — Exam1 vient de passer (status = past). Les items Densité étaient compressés (intervalles J+1 via Z1-AC08).
WHEN Exam1 passe en status = past (J+1 après exam_date).
THEN (1) Phase de repos : les items du chapitre Densité passent temporairement en intervalles de maintenance : SOLID → next_due_at = J+3, OK → next_due_at = J+2, FRAGILE → next_due_at = J+1. Ce « repos cognitif » dure le temps que le prochain exam (Exam2) entre dans la fenêtre de resserrement. (2) Recompression : dès que exam2_date - now ≤ 7 jours (soit le 15 mars, qui coïncide ici avec le passage d'Exam1), le resserrement Z1-AC08 se réactive pour les items Densité puisqu'Exam2 les référence aussi. Les items Densité reprennent des intervalles compressés. (3) Items non partagés : les items Forces (uniquement dans Exam2) restent compressés sans interruption si exam2_date - now ≤ 7j. (4) Règle générale : un item ne passe en maintenance longue (Z6-AC36) que lorsque aucun exam actif ne référence son chapitre (ou ses notions si notion_ids[] est renseigné). Le champ Mastery.next_due_at est recalculé à chaque changement de status d'un Exam.

NOTE : Le pattern reset + recompression respecte la science cognitive : après un contrôle, le cerveau bénéficie d'un bref repos avant de reprendre la compression. Ce repos est court (2-3 jours max, le temps que la fenêtre J-7 de l'exam suivant s'ouvre) et automatique. L'alternative « compression continue » (pas de repos) risque la fatigue ; l'alternative « maintenance définitive » risque l'oubli si Exam2 reteste le même contenu. Le reset + recompression est le juste milieu.

Z6-AC38 — Proposition de contrôle blanc : J-3 automatique + à la demande

Lot 0
GIVEN Un Exam « Contrôle Physique » a exam_date = 2026-03-20. L'Exam couvre 2 chapitres (18 items). L'élève n'a encore fait aucun mock_exam pour cet Exam. Nous sommes le 17 mars (J-3).
WHEN Le système évalue les exams à venir lors du calcul quotidien.
THEN (1) Proposition J-3 : une notification push est envoyée : « Contrôle de Physique dans 3 jours — fais un contrôle blanc pour te tester ! (~15 min) ». Le dashboard soirée (Z7-AC02) affiche un badge « Contrôle blanc disponible » sur l'exam avec un bouton direct « Lancer le contrôle blanc ». L'EveningPlan du soir intègre le mock_exam comme étape optionnelle (après le daily, pas en remplacement). (2) Rappel J-1 : si l'élève n'a pas fait de mock_exam, un rappel est envoyé : « Dernier soir pour un contrôle blanc avant le contrôle de Physique demain ! » Pas de rappel si le mock a déjà été fait. (3) À la demande : un bouton « Lancer un contrôle blanc » est toujours visible sur la page de chaque Exam actif, dès sa création (pas besoin d'attendre J-3). L'élève peut faire plusieurs mock_exams pour le même Exam (les questions varient grâce à la sélection aléatoire pondérée Z6-AC09). (4) Jour J : le mock_exam n'est plus proposé le jour de l'examen (Z6-AC36). (5) Durée : le mock_exam est cappé à 30 min max (Z6-AC09). La notification J-3 compte dans le plafond de 2 notifications par soirée (Z6-AC03).

NOTE : J-3 est le sweet spot : assez tôt pour identifier les faiblesses et avoir 2 jours pour les corriger, assez tard pour que le contenu soit frais. J-7 est trop tôt (l'élève n'a pas encore tout révisé et le résultat serait décourageant). J-1 est trop tard (pas le temps de corriger). Le bouton « à la demande » depuis la page exam couvre les élèves proactifs qui veulent s'entraîner sans attendre la notification.

Z6-AC39 — Parent multi-enfants : une notification par enfant

Lot 0
GIVEN Un parent a deux élèves liés : Lucas (4ème) et Emma (6ème). Lucas termine sa routine à 19h42 (3 matières, 15 min). Emma termine sa routine à 20h15 (2 matières, 10 min).
WHEN Les événements routine_completed sont émis pour chaque élève (Z7-AC05).
THEN Le parent reçoit une notification distincte par enfant, au moment où chaque routine est complétée. Multi-enfants + digest anticipé (Z8-AC06) : si le parent lie plusieurs enfants le même jour, un seul digest anticipé fusionné est envoyé (sections par enfant), pas un digest par enfant. Le champ parent.first_digest_sent_at est global au parent (pas par liaison) : 19h42 → « ✅ Lucas a terminé sa révision du soir : 3 matières, 15 min. » 20h15 → « ✅ Emma a terminé sa révision du soir : 2 matières, 10 min. » Chaque notification est envoyée indépendamment (pas d'agrégation, pas d'attente). Les préférences de notification (Z6-AC20) s'appliquent globalement (pas par enfant en MVP) : si le parent désactive routine_completed_enabled, c'est désactivé pour tous les enfants. Le digest hebdomadaire (Z6-AC27) contient une section par enfant avec le résumé individuel. Le dashboard parent affiche un sélecteur d'enfant pour naviguer entre les progressions. Si un seul enfant est lié, le comportement est identique (pas de sélecteur).

NOTE : Une notification par enfant est plus simple à implémenter et plus claire pour le parent : chaque signal est autonome et complet. L'agrégation en fin de soirée retarderait l'info (« Est-ce que Lucas a révisé ? ») et complexifierait la logique (attendre que tous aient fini, gérer les cas partiels). Le sélecteur d'enfant dans le dashboard est le seul point d'agrégation nécessaire en MVP.

Z6-AC40 — Timezone locale : détection automatique et configuration

Lot 0
GIVEN L'élève crée son compte depuis un appareil en timezone Europe/Paris (UTC+1 hiver, UTC+2 été). Un autre élève crée son compte depuis La Réunion (Indian/Reunion, UTC+4).
WHEN Le compte est créé (onboarding).
THEN Le système détecte automatiquement le timezone de l'appareil via l'API système (Intl.DateTimeFormat sur le client) et le stocke dans user.timezone (ex : Europe/Paris). Toutes les heures sont calculées en heure locale de l'utilisateur : notification_hour (défaut 18h30 locale), fenêtre de soirée evening_window_start/end (défaut 17h-22h locale), weekend_notification_hour (défaut 10h locale), digest parent (dimanche 9h locale), calcul de next_due_at (minuit local pour le « jour »). Le champ user.timezone est modifiable dans les paramètres (« Fuseau horaire »). Le serveur stocke toutes les dates en UTC et convertit à l'affichage et pour le scheduling des notifications. Si le timezone n'est pas détectable (cas rare), le fallback est Europe/Paris.

NOTE : La France métropolitaine est le marché principal, mais les DOM-TOM représentent ~3% de la population scolaire française et les lycées français à l'étranger sont un segment premium. Un élève à La Réunion qui reçoit sa notification à 18h30 heure de Paris (21h30 locale) ne révisera pas. Le coût d'implémentation est faible (détection auto + stockage d'un string timezone) et évite un problème UX silencieux mais fatal pour ces utilisateurs.

Z6-AC41 — Zone scolaire et calendrier de vacances intégré

Lot 0
GIVEN L'élève crée son compte (onboarding) ou uploade son premier chapitre (si la zone n'a pas encore été renseignée).
WHEN L'app détecte que user.school_zone est null.
THEN Un écran inline s'affiche : « Tu es en zone ? » avec les options : Zone A, Zone B, Zone C, DOM-TOM / Autre. La zone sélectionnée est stockée dans user.school_zone. Le système charge le calendrier des vacances scolaires correspondant pour l'année scolaire en cours (table SchoolHolidayPeriod : zone, name, start_date, end_date). Le calendrier est pré-intégré (données statiques ou API data.education.gouv.fr) et couvre : Toussaint, Noël, Hiver, Printemps, Été. Pour les zones A/B/C, les dates diffèrent pour Hiver et Printemps. Pour DOM-TOM, un calendrier spécifique par académie est intégré (Réunion, Guadeloupe, Martinique, Guyane, Mayotte). La zone est modifiable dans les paramètres (« Zone scolaire »). Le calendrier est mis à jour annuellement (arrêté ministériel publié ~2 ans à l'avance). Pendant une période de vacances (SchoolHolidayPeriod active) : (1) les capture_reminder et pre_class sont suspendus (pas de cours), (2) les daily sont maintenus selon la configuration vacances (Z6-AC42), (3) le dashboard affiche un bandeau « Mode vacances » avec la date de reprise des cours.

NOTE : Le calendrier scolaire français est prévisible et officiel — les dates sont publiées 2-3 ans à l'avance par le Ministère. L'intégrer (plutôt que de laisser l'élève activer/désactiver manuellement un « mode vacances ») est un gain UX majeur : aucune action requise de l'élève, le système sait quand sont les vacances. Le seul input nécessaire est la zone (3 options pour la métropole). Les vacances de Toussaint et Noël sont nationales (même dates, toutes zones) ; seuls Hiver et Printemps varient par zone. Le coût est une table de ~20 lignes/an, mise à jour une fois par an.

Z6-AC42 — Mode vacances : l'élève choisit ses jours et créneau de révision

Lot 0
GIVEN La période de vacances de Toussaint commence (détectée via SchoolHolidayPeriod active pour la zone de l'élève). L'élève n'a pas encore configuré ses préférences vacances.
WHEN L'élève ouvre l'app le premier jour des vacances (ou la veille au soir).
THEN Un écran s'affiche : « C'est les vacances ! Quand veux-tu réviser ? » avec : (1) Sélection des jours : grille Lu–Di avec cases à cocher. Pré-coché : lundi, mercredi, vendredi (3 jours, recommandation par défaut). L'élève peut modifier librement (0 à 7 jours). (2) Créneau préféré : slider 8h–20h (défaut : 10h). Ce créneau remplace notification_hour et evening_window pendant les vacances. (3) Bouton « C'est parti ! » pour confirmer ou « Pas maintenant » pour reporter (rappel le lendemain). Les préférences sont stockées dans VacationPreference : user_id, holiday_period_id, revision_days[] (tableau de day_of_week), preferred_hour, created_at. Pendant les vacances actives : les notifications de rappel de révision sont envoyées uniquement les jours sélectionnés, à l'heure choisie. Le concept de « soirée » disparaît : l'EveningPlan devient un « RevisionPlan » sans contrainte horaire soirée. Les jours sans révision prévue : aucune notification, aucune culpabilisation. Les items dues sont reportées au prochain jour de révision prévu. Si l'élève ne configure rien (« Pas maintenant » × 3) : fallback = notification quotidienne à weekend_notification_hour (10h), douce et non insistante. Les préférences vacances sont réutilisées pour la prochaine période de vacances (suggestion « Mêmes jours que la dernière fois ? » avec modification possible).

NOTE : Pendant les vacances, le rythme scolaire qui structure la soirée disparaît. Sans adaptation, l'app envoie des notifications « Tu as eu Maths aujourd'hui » un 28 décembre — incohérent et irritant. Le mode vacances respecte le rythme de l'élève (il choisit ses jours) tout en maintenant le lien avec l'app (les items dues continuent d'évoluer). La recommandation 3 jours/semaine est un bon compromis : assez fréquent pour maintenir la mémoire, assez espacé pour que ce soit des « vraies vacances ». Le fallback doux (notification quotidienne 10h) garantit que même un élève qui n'a pas configuré ses vacances reçoit un signal minimal sans agression.

Z6-AC43 — Créneaux d'indisponibilité récurrents (sport, activités)

Lot 0
GIVEN L'élève a entraînement de foot le mardi et le jeudi soir. Il configure ses créneaux d'indisponibilité dans les paramètres : mardi soir + jeudi soir marqués « indisponible ».
WHEN Le scheduler de notifications et l'EveningPlan s'exécutent un mardi soir.
THEN (1) Silence par défaut : aucune notification (capture_reminder, review_reminder, mock_exam) n'est envoyée les soirs indisponibles. Aucun EveningPlan n'est calculé par défaut. Le dashboard affiche « Bon entraînement ! 🏃 Ta prochaine révision est prévue [mercredi/demain]. » L'indisponibilité est une préférence forte, pas un blocage absolu — le scheduler de next_due_at interagit avec les créneaux selon les règles de Z6-AC46 (report de 1 jour max, pas de cumul excessif). (2) Redistribution : les items qui auraient été dues mardi sont automatiquement reportées au prochain soir disponible (mercredi). Le calcul de next_due_at tient compte des soirs indisponibles : si un item passe en next_due_at = mardi mais que mardi est bloqué, next_due_at est avancé à mercredi (pas de retard de plus de 1 jour — cf. Z6-AC46 pour les cas limites). Les sessions des soirs disponibles sont légèrement allongées pour compenser (~3-5 min de plus par soir d'indispo supplémentaire). (3) Configuration : stockée dans UnavailabilitySlot : user_id, day_of_week, label? (ex : « Football »), recurring (boolean). Les soirs bloqués sont affichés dans la vue agenda partiel (grisés). (4) Exception ponctuelle : bouton « Pas d'entraînement ce soir » sur le dashboard (si soir normalement indisponible). Réactive l'EveningPlan pour cette soirée uniquement. L'exception inverse est aussi possible : « Ce soir je ne peux pas » sur un soir normalement disponible (crée une UnavailabilityException ponctuelle). (5) Impact parent : le digest parent (Z6-AC27) mentionne les soirs sautés pour cause d'activité (pas de signal d'alarme — c'est prévu et normal).

NOTE : Un collégien qui fait du sport 2-3 soirs/semaine est le profil le plus courant ET le plus à risque de churn : il rentre à 20h, a ses devoirs à faire, et reçoit une notification « Révise ta Physique ! » — il désinstalle. Le silence intelligent les soirs de sport est un anti-churn majeur. La redistribution automatique garantit que la couverture SRS est maintenue malgré les soirs manqués, sans effort cognitif de l'élève. Le message « Bon entraînement ! » transforme un moment d'absence en signal positif — l'app comprend la vie de l'élève.

Z6-AC44 — Liaison parent-élève via code 6 caractères

Lot 0
GIVEN L'élève a un compte actif. Le parent crée son propre compte (rôle parent) et souhaite lier son compte à l'élève pour accéder au dashboard parent (Z6-AC27 digest hebdo, Z6-AC28 signalement OCR).
WHEN Le parent initie la liaison depuis son interface (bouton « Lier un élève ») ou l'élève initie l'invitation depuis ses paramètres (bouton « Inviter un parent »).
THEN (1) Génération du code : un code alphanumérique de 6 caractères (majuscules + chiffres, sans ambiguïté : pas de 0/O, 1/I/l) est généré et affiché à l'écran de l'élève (ou envoyé par l'élève au parent via un lien de partage). Le code est valide 24h (PairingCode.expires_at = now + 24h). Un seul code actif par élève à la fois (re-génération = invalidation du précédent). (2) Saisie par le parent : le parent saisit le code dans son interface. Le système vérifie : code valide, non expiré, non déjà utilisé. En cas d'erreur, message clair : « Code invalide ou expiré. Demande un nouveau code à ton enfant. » Maximum 5 tentatives en 15 min (anti-bruteforce). (3) Confirmation côté élève : après saisie correcte par le parent, l'élève reçoit une notification : « [Prénom du parent] souhaite suivre tes révisions. Accepter ? ». L'élève doit confirmer la liaison (pas de liaison automatique — respect de l'autonomie). (4) Liaison active : une fois confirmée, le parent accède au dashboard parent avec les stats de l'élève. L'élève voit dans ses paramètres : « Suivi par [Prénom du parent] ». Un bouton « Retirer l'accès » est disponible à tout moment pour l'élève. (5) Multi-parent : un élève peut être lié à 2 parents maximum. Chaque parent a un accès identique. (6) Entité : PairingCode { id, student_user_id, code (6 chars), expires_at, used_by_parent_id?, used_at?, status (active|used|expired) }. Relation parent-élève : ParentLink { id, parent_user_id, student_user_id, created_at, active (boolean) }.

NOTE : Le code 6 caractères est le mécanisme le plus simple et le plus sécurisé pour le contexte père-fils : pas de scan QR (le parent n'a pas forcément l'app installée au moment de la liaison), pas de partage d'email (l'élève n'a pas toujours d'email), pas de deep link (cassé sur certains navigateurs). Le code verbal (« Papa, tape ABC123 ») est le canal naturel dans un foyer. La confirmation côté élève est critique : un adolescent doit sentir qu'il contrôle qui accède à ses données. Sans cette confirmation, le système serait perçu comme de la surveillance, pas du coaching.

Z6-AC45 — Mode vacances : configuration du rythme de révision

Lot 0
GIVEN Une période de vacances scolaires est détectée (via SchoolHolidayPeriod correspondant à la zone de l'élève). L'élève n'a pas encore configuré ses préférences pour cette période.
WHEN L'élève ouvre l'app pendant la première journée des vacances (ou la veille au soir si dans la fenêtre evening_window).
THEN Un écran de configuration s'affiche : « C'est les vacances ! Quand veux-tu réviser ? ». (1) Sélection des jours : grille Lu–Di avec cases à cocher. Pré-coché par défaut : lundi, mercredi, vendredi (3 jours, recommandation basée sur la recherche SRS). L'élève peut modifier librement (0 à 7 jours). Si 0 jour sélectionné : message doux « Tu es sûr(e) ? Même 1 session par semaine aide à ne pas oublier. » mais pas de blocage. (2) Créneau horaire : slider 8h–20h, défaut 10h. Ce créneau remplace evening_window_start/end et notification_hour pendant les vacances. (3) Confirmation : bouton « C'est parti ! » ou « Pas maintenant » (rappel le lendemain, max 3 rappels puis fallback silencieux). (4) Stockage : VacationPreference { user_id, holiday_period_id, revision_days[], preferred_hour, created_at }. (5) Effets : pendant les vacances actives, les notifications ne sont envoyées que les jours sélectionnés, à l'heure choisie. L'EveningPlan devient un « RevisionPlan » sans contrainte soirée. Les jours sans révision : silence total, aucune culpabilisation. Les items dues sont reportées au prochain jour de révision prévu. (6) Réutilisation : les préférences sont proposées pour la prochaine période de vacances (« Mêmes jours que la dernière fois ? »).

NOTE : Pendant les vacances, le rythme scolaire qui structure la soirée disparaît. Sans adaptation, l'app envoie des notifications « Tu as eu Maths aujourd'hui » un 28 décembre — incohérent et irritant. Le mode vacances respecte le rythme de l'élève tout en maintenant le lien avec l'app. La recommandation 3 jours/semaine est un compromis entre maintien de la mémoire et repos réel. Le fallback doux garantit qu'un élève qui n'a pas configuré ses vacances reçoit un signal minimal sans agression.

Z6-AC46 — Interaction scheduler avec les créneaux d'indisponibilité

Lot 0
GIVEN L'élève a configuré des créneaux d'indisponibilité récurrents (Z6-AC43) : mardi et jeudi soirs marqués « Football ». Un item passe en next_due_at = mardi (jour indisponible). Un examen est posé jeudi avec resserrement d'intervalles (Z1-AC08).
WHEN Le scheduler de next_due_at calcule la prochaine date de révision pour un item.
THEN (1) Report automatique : si next_due_at tombe un soir indisponible, la date est avancée au prochain soir disponible (pas reculée). Exemple : next_due_at = mardi → avancé à mercredi. Le report est de 1 jour maximum — si mercredi est aussi indisponible, l'item est planifié mercredi quand même (l'indisponibilité n'est pas un blocage absolu, c'est une préférence). (2) Interaction avec exam : si le resserrement d'intervalles (Z1-AC08) place un next_due_at sur un soir indisponible, la même règle s'applique (avancé au prochain soir dispo). Le resserrement ne peut pas être annulé par l'indisponibilité — l'item est juste décalé d'un jour. (3) Compensation : les sessions des soirs disponibles qui suivent un soir indisponible sont légèrement allongées pour absorber les items reportés (+2-4 questions, ~3-5 min de plus). L'estimation de durée dans l'EveningPlan reflète cette compensation. (4) Pas de cumul excessif : si les reports accumulés font passer une session au-dessus de 20 min estimés, les items les moins urgents (SOLID dues) sont reportés au surlendemain. (5) Transparence : dans le dashboard soirée, les items reportés depuis un soir indisponible sont annotés : « Reporté de mardi (football) ». Le parent voit dans le digest : « Mardi et jeudi : pas de révision (activités). Compensé mercredi et vendredi. »

NOTE : L'interaction entre indisponibilité et SRS est le cas le plus fréquent de conflit de scheduling : un collégien sportif a 2-3 soirs bloqués/semaine, et le SRS planifie des rappels sans en tenir compte. Sans cette logique de report, l'élève accumule du retard les soirs de sport et se retrouve avec une session de 30 min le mercredi — ce qui est décourageant et inefficace. Le report de 1 jour max et la compensation progressive maintiennent le rythme SRS sans surcharger les soirs disponibles.