{
  "contract_version": "gab_391_contract_v1",
  "gab_id": "GAB-391",
  "canonical_name": "DataLearningIntro",
  "module_owner": "EdTechDataLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "data_learning_id",
    "title",
    "data_ref",
    "data_type",
    "learning_goal",
    "primary_cta"
  ],
  "optional_fields": [
    "intro_body",
    "source_notice",
    "unit_notice",
    "uncertainty_notice",
    "accessibility_table_ref",
    "fallback_action",
    "fallback_text_summary"
  ],
  "field_types": {
    "title": "string — titre de l'activité données affiché dans le hero et la carte",
    "data_ref": "string — identifiant du jeu de données ciblé (pointeur, pas la donnée elle-même)",
    "data_type": "enum['table','bar_chart','line_chart','pie_chart','scatter','map_data','timeline_data','dataset','mixed','unknown']",
    "learning_goal": "enum['read','compare','calculate','detect_trend','interpret','argue','verify','spot_error'] — objectif pédagogique de l'activité",
    "intro_body": "string — texte d'introduction (pourquoi lire avant d'interpréter)",
    "primary_cta": "string — libellé du bouton qui lance la lecture",
    "source_notice": "object|null {label:string, text:string} — mention de source, requise si données chiffrées",
    "unit_notice": "object|null {label:string, text:string} — mention d'unité, requise si données chiffrées",
    "uncertainty_notice": "object|null {label:string, text:string} — mention d'incertitude/échelle si pertinent",
    "accessibility_table_ref": "string — description ou référence d'accès clavier/lecteur d'écran du jeu de données",
    "fallback_action": "object|null {id:string, label:string, feedback_type:enum['ok','warn','note'], feedback_text:string} — action de repli si dataset indisponible",
    "fallback_text_summary": "string — description textuelle de secours accessible"
  },
  "constraints": [
    "data_type et learning_goal sont contraints à leurs énumérations respectives.",
    "Source et unité (source_notice + unit_notice) requises si data_type implique des valeurs chiffrées (table, bar_chart, line_chart, pie_chart, scatter, dataset).",
    "L'intro ne doit pas interpréter ni conclure : elle annonce l'objectif et lance la lecture.",
    "Accessibilité : accessibility_table_ref requise pour tout graphe ou tableau important.",
    "Fallback obligatoire (fallback_action) si le dataset/graphe peut être indisponible — aucune valeur inventée.",
    "data_ref est un pointeur vers le jeu de données ; la donnée réelle vit dans les instance banks, pas ici."
  ],
  "blocked_conditions": [
    "title absent (BLOCKED)",
    "data_learning_id absent (BLOCKED)",
    "data_ref absent (BLOCKED)",
    "data_type absent ou hors énumération (BLOCKED)",
    "learning_goal absent ou hors énumération (BLOCKED)",
    "primary_cta absent (BLOCKED)",
    "data_type chiffré sans source_notice ou sans unit_notice (BLOCKED)"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "fallback_text"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet : hero + carte intro + notices source/unité + chip objectif + CTA actif" },
    { "case": "champ requis manquant (title/data_ref/data_type/learning_goal/primary_cta)", "expected": "BLOCKED listant le champ absent" },
    { "case": "data_type chiffré sans source ou unité", "expected": "BLOCKED — source et unité requises pour données chiffrées" },
    { "case": "data_type hors énumération", "expected": "BLOCKED — valeur non autorisée" },
    { "case": "clic primary_cta", "expected": "passage à l'état started, panneau de lancement affiché sans modifier le HTML" },
    { "case": "fallback_action présent et cliqué", "expected": "panneau warn affiché, aucune valeur de données inventée" },
    { "case": "instance externe injectée via init(ext)", "expected": "le rendu change sans modifier le HTML (critère d'or)" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal, notices passent en colonne mobile" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-391",
    "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)."
  }
}
