{
  "contract_version": "gab_154_contract_v1",
  "gab_id": "GAB-154",
  "canonical_name": "GameActionChallenge",
  "module_owner": "EdTechGameLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "challenge_id",
    "title",
    "instruction",
    "items",
    "feedback_init",
    "feedback_ok",
    "feedback_ko"
  ],
  "optional_fields": [
    "lives",
    "feedback_game_over",
    "feedback_win",
    "primary_cta"
  ],
  "field_types": {
    "challenge_id": "string — identifiant unique de l'instance de jeu",
    "title": "string — titre affiché dans le HUD/hero",
    "instruction": "string — consigne affichée au-dessus des cibles dans l'arène",
    "items": "array<{label:string, is_target:boolean}> — au moins 2 items, dont au moins 1 is_target:true",
    "lives": "integer(1..5) — nombre de vies initiales (défaut : 3)",
    "feedback_init": "string — texte de pied de page avant toute action",
    "feedback_ok": "string(html) — feedback affiché après bonne réponse",
    "feedback_ko": "string(html) — feedback affiché après erreur",
    "feedback_game_over": "string(html) — feedback quand vies = 0",
    "feedback_win": "string(html) — feedback fin de mini-jeu réussi ; {score}/{lives} interpolés",
    "primary_cta": "object{label:string, action:string} — libellé du bouton restart"
  },
  "constraints": [
    "items doit contenir au moins 1 item avec is_target:true (sinon le jeu n'est pas finissable).",
    "lives doit être entre 1 et 5 si présent.",
    "Ce jeu est ludique et NON NOTÉ au bulletin — ne pas utiliser pour exercices chronométrés notés (→ GAB-133).",
    "feedback_win peut contenir les placeholders {score} et {lives} qui sont interpolés par le moteur."
  ],
  "blocked_conditions": [
    "items absent ou tableau vide",
    "aucun item avec is_target:true",
    "instruction absente"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria-label sur les boutons cibles"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, jeu jouable, 0 erreur" },
    { "case": "items absent ou vide", "expected": "BLOCKED listant le champ manquant" },
    { "case": "aucun item is_target:true", "expected": "BLOCKED — jeu non finissable" },
    { "case": "instruction absente", "expected": "BLOCKED" },
    { "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": "vies épuisées", "expected": "feedback_game_over affiché, jeu verrouillé" },
    { "case": "tous les is_target:true cliqués", "expected": "feedback_win avec score/lives interpolés" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-154",
    "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)."
  }
}
