{
  "contract_version": "gab_308_contract_v1",
  "gab_id": "GAB-308",
  "canonical_name": "DocumentLearningChartAnalysis",
  "module_owner": "EdTechDocumentLearning",
  "renderer_key": "media_viewer",
  "required_fields": [
    "gab_id",
    "chart_analysis_id",
    "data_points",
    "axis_verify_cta_label"
  ],
  "optional_fields": [
    "title",
    "chart_title",
    "chart_type",
    "axis_x_label",
    "axis_y_label",
    "axis_feedback",
    "axis_verified_label",
    "completion_trigger",
    "fallback_text_summary"
  ],
  "field_types": {
    "chart_analysis_id": "string — identifiant unique de l'instance graphique",
    "chart_type": "enum['bar','line','pie'] — type de graphique ; 'bar' par défaut si absent",
    "data_points": "array<{label:string, height_pct:number(1..100), value:number, unit:string, is_peak:boolean, bar_feedback:string, hint_suffix:string}> — au moins 1 entrée",
    "axis_verify_cta_label": "string — libellé du bouton de vérification des axes",
    "axis_feedback": "string (HTML admis) — texte affiché au clic 'vérifier axes'",
    "axis_verified_label": "string — libellé du bouton après vérification",
    "completion_trigger": "enum['peak_bar_clicked','axis_verified','any_bar_clicked'] — condition de complétion",
    "fallback_text_summary": "string — texte alternatif accessibilité / no-JS"
  },
  "constraints": [
    "data_points doit contenir au moins 1 entrée.",
    "height_pct dans [1, 100] : proportionnel à la valeur max du graphique.",
    "is_peak:true sur exactement une barre (le pic) — si plusieurs, le moteur sélectionne le premier.",
    "bar_feedback : extrait du contenu source, jamais inventé.",
    "axis_verify_cta_label : libellé bouton venant du JSON, jamais en dur dans le HTML."
  ],
  "blocked_conditions": [
    "data_points absent ou vide (BLOCKED)",
    "chart_analysis_id absent (BLOCKED)",
    "gab_id absent (BLOCKED)",
    "axis_verify_cta_label absent (BLOCKED)"
  ],
  "accessibility": [
    "keyboard_navigable (tabindex sur chaque barre)",
    "aria-label sur chaque barre (valeur + unité + pic si applicable)",
    "role=list / listitem sur le conteneur de barres",
    "prefers_reduced_motion (transitions réduites)",
    "fallback_text_summary (no-JS / lecteur d'écran)"
  ],
  "qa_cases": [
    { "case": "instance conforme (3 barres, 1 pic)", "expected": "graphique rendu, 0 erreur, barre pic cliquable valide" },
    { "case": "champ requis manquant (data_points vide)", "expected": "panel BLOCKED lisible, graphique non rendu" },
    { "case": "clic barre non-pic", "expected": "panel note + hint_suffix visible" },
    { "case": "clic barre pic", "expected": "panel ok, complétion visuelle" },
    { "case": "clic 'Vérifier axes'", "expected": "panel note avec axis_feedback, bouton désactivé + axis_verified_label" },
    { "case": "instance externe injectée via ENGINE.init(ext)", "expected": "rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal, barres visibles" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-308",
    "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)."
  }
}
