{
  "contract_version": "gab_128_contract_v1",
  "gab_id": "GAB-128",
  "canonical_name": "ExerciseStepByStepSolution",
  "module_owner": "EdTechExerciseLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "exercise_id",
    "problem_text",
    "steps",
    "cta_initial",
    "primary_cta"
  ],
  "optional_fields": [
    "title",
    "problem_label",
    "cta_next",
    "cta_penultimate",
    "cta_restart"
  ],
  "field_types": {
    "exercise_id": "string — identifiant unique de l'exercice source",
    "problem_text": "string — énoncé du problème affiché en tête de carte",
    "problem_label": "string — libellé du chapeau (défaut : 'Problème')",
    "steps": "array<{number:string, title:string, description:string, is_final:boolean}> — au moins 1 étape requise",
    "cta_initial": "string — libellé du bouton avant la 1ʳᵉ révélation",
    "cta_next": "string — libellé entre la 1ʳᵉ et l'avant-dernière étape",
    "cta_penultimate": "string — libellé quand l'avant-dernière étape est visible",
    "cta_restart": "string — libellé quand toutes les étapes sont révélées",
    "primary_cta": "object{label:string, action:string}"
  },
  "constraints": [
    "steps doit contenir au moins 1 objet {number, title, description, is_final}.",
    "Exactement 1 étape avec is_final:true (la conclusion, affichée avec style doré).",
    "Les descriptions peuvent contenir du HTML inline limité (<span class=\"formula\">, <b>, <em>) — aucune balise block.",
    "primary_cta.label reflète cta_initial à l'état initial.",
    "cta_initial obligatoire ; cta_next / cta_penultimate / cta_restart optionnels avec valeurs par défaut dans le moteur."
  ],
  "blocked_conditions": [
    "steps absent ou vide (BLOCKED)",
    "problem_text absent (BLOCKED)",
    "exercise_id absent (BLOCKED)"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria_live_region_pour_etape_revelee"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, bouton progressif fonctionnel, 0 erreur" },
    { "case": "steps vide []", "expected": "BLOCKED listant 'steps absent ou vide'" },
    { "case": "problem_text absent", "expected": "BLOCKED listant 'problem_text absent'" },
    { "case": "clic bouton séquence complète", "expected": "toutes les étapes révélées, bouton passe en mode restart" },
    { "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, connecteur vertical intact" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-128",
    "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)."
  }
}
