{
  "contract_version": "gab_107_contract_v1",
  "gab_id": "GAB-107",
  "canonical_name": "MemoryOrderingSequence",
  "module_owner": "EdTechMemoryLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "ordering_id",
    "instruction",
    "items"
  ],
  "optional_fields": [
    "title",
    "feedback",
    "primary_cta"
  ],
  "field_types": {
    "ordering_id": "string — identifiant unique de l'exercice d'ordre",
    "instruction": "string — consigne affichée au-dessus de la liste",
    "items": "array<{id:string, text:string, correct_position:integer}> — liste des items à ordonner (présentés dans un ordre initial quelconque ; correct_position = rang attendu dans l'ordre final, 1-based)",
    "feedback": "object{success:string, error:string} — messages affichés après vérification",
    "primary_cta": "object{label:string, action:string} — libellé du bouton de vérification"
  },
  "constraints": [
    "items ne doit pas être vide — au moins 2 éléments pour que l'exercice ait du sens.",
    "correct_position est 1-based et doit couvrir exactement les rangs 1..N (N = nombre d'items).",
    "instruction est la consigne affichée ; elle vient du JSON, jamais en dur dans le HTML.",
    "primary_cta.label fournit le libellé du bouton structurel ; valeur par défaut 'Vérifier' si absent."
  ],
  "blocked_conditions": [
    "instruction absente",
    "items vides"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "aria_labels_on_arrows",
    "prefers_reduced_motion"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, liste d'items, bouton libellé depuis JSON, 0 erreur" },
    { "case": "champ requis manquant (instruction)", "expected": "BLOCKED listant le champ absent" },
    { "case": "items vides", "expected": "BLOCKED items vides" },
    { "case": "vérification ordre correct", "expected": "items passent en .correct (mint) + feedback.success affiché" },
    { "case": "vérification ordre incorrect", "expected": "items incorrects restent neutres + feedback.error affiché" },
    { "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" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-107",
    "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)."
  }
}
