{
  "contract_version": "gab_152_contract_v1",
  "gab_id": "GAB-152",
  "canonical_name": "GameMissionCard",
  "module_owner": "EdTechGameLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "mission_card_id",
    "mission_number",
    "title",
    "objective",
    "cta_label"
  ],
  "optional_fields": [
    "mission_icon",
    "xp_reward_ribbon",
    "success_condition",
    "constraint",
    "reward",
    "cta_feedback"
  ],
  "field_types": {
    "mission_number": "string — ex: 'Mission #03'",
    "mission_icon": "string — emoji ou URL icône",
    "title": "string — titre court de la mission",
    "xp_reward_ribbon": "string — ex: '+15 XP', affiché en ruban décoratif, absent = ruban masqué",
    "objective": "string — texte de l'objectif pédagogique",
    "success_condition": "string — critère de réussite de la mission",
    "constraint": "string — contrainte/limite (erreurs max, temps, etc.)",
    "reward": "string — récompense (XP, badge, déblocage)",
    "cta_label": "string — libellé du bouton d'acceptation",
    "cta_feedback": "string — message affiché après clic sur le bouton"
  },
  "constraints": [
    "mission_number : chaîne libre mais conventionnellement 'Mission #NN'.",
    "xp_reward_ribbon : si absent, le ruban décoratif est masqué (hidden).",
    "cta_label : bouton structurel (HTML), libellé venant du JSON.",
    "success_condition, constraint, reward : optionnels individuellement ; si aucun des trois n'est fourni, les rangées correspondantes sont masquées.",
    "mission_icon : emoji par défaut '📜' si absent."
  ],
  "blocked_conditions": [
    "mission_number absent",
    "title absent",
    "objective absent",
    "cta_label absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria-label sur le bouton CTA"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet avec toutes les rangées, 0 erreur" },
    { "case": "champ requis manquant (title absent)", "expected": "BLOCKED listant le champ" },
    { "case": "xp_reward_ribbon absent", "expected": "ruban masqué, reste du rendu intact" },
    { "case": "success_condition + constraint + reward absents", "expected": "rangées optionnelles masquées, objective + CTA toujours visibles" },
    { "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-152",
    "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)."
  },
  "external_refs": {
    "dependencies": [
      "ENG-002 (intro session moteur — cas d'usage adjacent, non requis ici)",
      "GAB-045 (intro aventure narrative — cas d'usage adjacent)"
    ]
  }
}
