{
  "contract_version": "gab_385_contract_v1",
  "gab_id": "GAB-385",
  "canonical_name": "MediaLearningTranscriptSync",
  "module_owner": "EdTechMediaLearning",
  "renderer_key": "media_viewer",
  "required_fields": [
    "gab_id",
    "transcript_sync_id",
    "segments"
  ],
  "optional_fields": [
    "title",
    "media_hint",
    "export_label",
    "export_format",
    "use_when",
    "do_not_use_when",
    "slots",
    "accessibility",
    "child_safety"
  ],
  "field_types": {
    "segments": "array<{time:string, text:string, confidence:enum['high','low']}> — au moins 1 segment requis",
    "transcript_sync_id": "string — identifiant unique de la session de synchro",
    "export_format": "enum['txt','vtt','srt'] — format d'export du transcript",
    "accessibility": "object{keyboard_navigable:boolean, focus_visible:boolean, prefers_reduced_motion:boolean, low_confidence_warning:string}",
    "child_safety": "object{no_autoplay:boolean, no_invented_transcript:boolean, low_confidence_flagged:boolean}",
    "slots": "object{required:array<string>, optional:array<string>}"
  },
  "constraints": [
    "segments doit contenir au moins 1 élément (sinon BLOCKED).",
    "Chaque segment porte time (ex: '0:00'), text et confidence ('high'|'low').",
    "confidence:'low' déclenche un avertissement visuel et sonore — jamais masqué.",
    "Aucun contenu textuel n'est inventé : le moteur affiche uniquement ce que le JSON contient.",
    "no_autoplay obligatoire par design : l'élève déclenche la lecture."
  ],
  "blocked_conditions": [
    "gab_id absent",
    "transcript_sync_id absent",
    "segments vides ou absents"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria_label_on_segments",
    "low_confidence_warning_text"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, 0 erreur, 3 segments affichés" },
    { "case": "champ requis manquant (transcript_sync_id absent)", "expected": "BLOCKED listant le champ" },
    { "case": "segments vides []", "expected": "BLOCKED listant segments vides" },
    { "case": "segment confidence:'low'", "expected": "badge ⚠ bas visible + panel warn au clic" },
    { "case": "clic segment high", "expected": "panel ok avec time et texte du segment" },
    { "case": "bouton export", "expected": "transcript formaté affiché avec timestamps" },
    { "case": "instance externe injectée via init(ext)", "expected": "le rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal" },
    { "case": "navigation clavier (Tab + Enter/Space)", "expected": "chaque segment activable sans souris" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-385",
    "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). renderer_key 'media_viewer' sans moteur kit de référence — renderer dérivé de l'écran source INDEX-300-medialearning-GAB-381-385-PLAYABLE.html, stage data-tpl='385', handlers d385Seg/d385Export."
  }
}
