{
  "contract_version": "gab_300_contract_v1",
  "gab_id": "GAB-300",
  "canonical_name": "OralLearningFinalRehearsal",
  "module_owner": "EdTechOralLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "final_rehearsal_id",
    "oral_context_ref",
    "rehearsal_checklist",
    "completion_policy",
    "next_action"
  ],
  "optional_fields": [
    "title",
    "subtitle",
    "milestone_banner",
    "recording_policy",
    "timer_policy",
    "final_plan_ref",
    "self_review_ref",
    "teacher_review_ref",
    "privacy_notice",
    "source_metadata",
    "non_certification_notice",
    "reason_block"
  ],
  "field_types": {
    "final_rehearsal_id": "string — identifiant unique de la session de répétition finale",
    "oral_context_ref": "string — référence au contexte oral (ex: DNB_oral_séance_finale)",
    "rehearsal_checklist": "object{heading:string, items:array<{label:string, done:boolean}>}",
    "completion_policy": "enum['non_certifying'] — toujours non_certifying (doctrine NON-CERTIFICATION)",
    "next_action": "object{primary_cta_label, primary_cta_confirm, secondary_cta_label, secondary_cta_reveal}",
    "milestone_banner": "object{icon:string, label:string, text:string}",
    "recording_policy": "object{options:array<{id,icon,label,detail}>}",
    "timer_policy": "object{duration_min:number, label:string}",
    "privacy_notice": "object{heading:string, bullets:array<string>}",
    "non_certification_notice": "object{heading:string, text:string}",
    "reason_block": "object{heading:string, text:string}",
    "source_metadata": "object{module,lot,vague,safety_doctrine}"
  },
  "constraints": [
    "completion_policy DOIT être 'non_certifying' — doctrine NON-CERTIFICATION absolue.",
    "rehearsal_checklist.items doit contenir au moins 1 item done:false (répétition à faire).",
    "next_action.primary_cta_label vient du JSON — jamais en dur dans le HTML.",
    "privacy_notice.bullets affichés tels quels — jamais modifiés par le moteur.",
    "Le renderer n'attribue jamais de note, badge ou certificat (garde-fou child_safety)."
  ],
  "blocked_conditions": [
    "final_rehearsal_id absent (BLOCKED)",
    "oral_context_ref absent (BLOCKED)",
    "rehearsal_checklist absent ou items vides (BLOCKED)",
    "completion_policy absent (BLOCKED)",
    "next_action absent (BLOCKED)"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "fallback_text_summary via subtitle"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, 0 erreur, checklist 6 items" },
    { "case": "champ requis manquant", "expected": "BLOCKED listant le champ absent" },
    { "case": "completion_policy absent", "expected": "BLOCKED — doctrine non-certification non applicable" },
    { "case": "rehearsal_checklist.items vide", "expected": "BLOCKED — checklist vide non conforme" },
    { "case": "instance externe injectée via init(ext)", "expected": "le rendu change sans modifier le HTML" },
    { "case": "recording_policy opt-in sélectionné", "expected": "feedback choix affiché sous les boutons" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal, fr-options en 1 col sur mobile" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-300",
    "note": "Ce schema VALIDE l'instance. Le contrat pédagogique complet (input_contract/validation_logic/feedback_scoring_logic) vit dans le CORE-GAB officiel, pas ici (évite la duplication)."
  }
}
