{
  "contract_version": "gab_047_contract_v1",
  "gab_id": "GAB-047",
  "canonical_name": "StoryKettyDialogue",
  "module_owner": "EdTechStoryLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "dialogue_id",
    "items"
  ],
  "optional_fields": [
    "title",
    "persona_ketty",
    "persona_eleve",
    "replay_cta",
    "animation"
  ],
  "field_types": {
    "dialogue_id": "string — identifiant unique de l'instance de dialogue",
    "items": "array<{role:enum['ketty','eleve'], text:string}> — répliques ordonnées, non vide",
    "persona_ketty": "object{name:string, avatar:string, bubble_style:string}",
    "persona_eleve": "object{name:string, avatar:string, bubble_style:string}",
    "replay_cta": "object{label:string, icon:string}",
    "animation": "object{initial_delay_ms:number, step_delay_ms:number}"
  },
  "constraints": [
    "items doit contenir au moins 1 réplique.",
    "role de chaque item : 'ketty' ou 'eleve' uniquement.",
    "Les textes viennent exclusivement de l'instance — interdiction d'écrire du contenu pédagogique en dur dans le HTML.",
    "replay_cta.label pilote le libellé du bouton ; si absent, le moteur utilise un fallback 'Rejouer le dialogue'."
  ],
  "blocked_conditions": [
    "gab_id absent",
    "dialogue_id absent",
    "items vides ou absents"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria_hidden sur les avatars décoratifs"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, 3 répliques animées, bouton rejouer fonctionnel" },
    { "case": "items absent ou vide", "expected": "BLOCKED listant le champ manquant, aucun rendu" },
    { "case": "dialogue_id absent", "expected": "BLOCKED listant le champ manquant" },
    { "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, bulles max-width 82% sur mobile" },
    { "case": "clic Rejouer", "expected": "les répliques disparaissent puis réapparaissent en cascade" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-047",
    "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)."
  }
}
