{
  "contract_version": "gab_299_contract_v1",
  "gab_id": "GAB-299",
  "canonical_name": "OralLearningAnswerFollowUp",
  "module_owner": "EdTechOralLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "follow_up_id",
    "initial_answer_ref",
    "follow_up_question",
    "follow_up_type",
    "expected_improvement"
  ],
  "optional_fields": [
    "title",
    "subtitle",
    "exchange_thread",
    "follow_up_counter",
    "max_follow_ups",
    "anti_trap_rules",
    "why_max_3",
    "primary_cta",
    "secondary_cta",
    "hint_allowed",
    "difficulty",
    "feedback_ref",
    "answer_frame",
    "source_metadata"
  ],
  "field_types": {
    "follow_up_type": "enum['clarify','deepen','example','approfondissement']",
    "exchange_thread": "array<{role:enum['student','jury'],label:string,text:string,tag?:enum['CLARIFY','DEEPEN','EXAMPLE']}>",
    "follow_up_counter": "integer >= 0",
    "max_follow_ups": "integer >= 1",
    "anti_trap_rules": "array<string>",
    "primary_cta": "object{label,action}",
    "secondary_cta": "object{label,action}",
    "difficulty": "integer(1..3)"
  },
  "constraints": [
    "max_follow_ups ne peut pas dépasser 3 (doctrine anti-intimidation).",
    "follow_up_counter doit être inférieur ou égal à max_follow_ups.",
    "Les relances ne peuvent pas piéger, contredire systématiquement, ni punir un 'je ne sais pas' sincère.",
    "follow_up_type : limité aux types constructifs (clarify/deepen/example).",
    "primary_cta : bouton structurel (HTML), libellé venant du JSON."
  ],
  "blocked_conditions": [
    "follow_up_id absent",
    "initial_answer_ref absent",
    "follow_up_question absent",
    "follow_up_type absent",
    "expected_improvement absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria_labels_on_buttons"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, thread affiché, compteur correct, 0 erreur" },
    { "case": "champ requis manquant (ex: follow_up_question)", "expected": "BLOCKED listant le champ" },
    { "case": "follow_up_counter == max_follow_ups", "expected": "bouton 'Continuer' désactivé ou masqué" },
    { "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 lisibles" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-299",
    "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)."
  }
}
