{
  "contract_version": "gab_198_contract_v1",
  "gab_id": "GAB-198",
  "canonical_name": "PathMapReviewLoop",
  "module_owner": "EdTechPathMap",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "review_loop_id",
    "title",
    "issue_text",
    "steps",
    "primary_cta"
  ],
  "optional_fields": [
    "subtitle",
    "issue_label",
    "anti_loop_notice",
    "skip_cta"
  ],
  "field_types": {
    "review_loop_id": "string — identifiant unique de la boucle (slug)",
    "title": "string — titre affiché dans l'en-tête de la carte",
    "subtitle": "string — sous-titre optionnel (ex: '3 micro-étapes · pas punitif')",
    "issue_label": "string — libellé du bandeau issue (ex: 'Point à consolider')",
    "issue_text": "string — description du point de fragilité détecté",
    "steps": "array<{index:number, label:string, module_type:enum['visual','memory','exercise','quiz','story'], module_badge:string, duration_label:string}>",
    "anti_loop_notice": "string — garantie anti-répétition affichée en bas des étapes",
    "primary_cta": "object{label:string, action:string}",
    "skip_cta": "object{label:string, action:string}"
  },
  "constraints": [
    "steps doit contenir au moins 1 élément et au plus 5.",
    "primary_cta.label et primary_cta.action sont tous deux obligatoires.",
    "module_type enum : visual, memory, exercise, quiz, story — toute autre valeur est rendue comme badge neutre.",
    "index dans steps doit être un entier >= 0 ; les étapes sont rendues dans l'ordre du tableau.",
    "anti_loop_notice : jamais une promesse de résultat — seulement une garantie de diversité d'approche."
  ],
  "blocked_conditions": [
    "steps absent ou tableau vide",
    "issue_text absent",
    "primary_cta absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria_labels_steps"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, 3 étapes cliquables, 0 erreur" },
    { "case": "steps vide ([])", "expected": "BLOCKED listant le champ manquant" },
    { "case": "issue_text absent", "expected": "BLOCKED" },
    { "case": "primary_cta absent", "expected": "BLOCKED" },
    { "case": "étape marquée done au clic", "expected": "marqueur passe à ✓, fond mint" },
    { "case": "toutes étapes done", "expected": "CTA primaire passe en état 'terminé'" },
    { "case": "instance externe injectée", "expected": "rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "CTAs empilés sur mobile, aucun débordement" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-198",
    "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)."
  }
}
