{
  "contract_version": "gab_328_contract_v1",
  "gab_id": "GAB-328",
  "canonical_name": "ResearchLearningSynthesisGrid",
  "module_owner": "EdTechResearchLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "synthesis_grid_id",
    "grid_rows",
    "primary_cta",
    "answer_plan"
  ],
  "optional_fields": [
    "title",
    "instruction",
    "contradiction_warning",
    "cta_locked_label",
    "cta_done_label",
    "fallback_text_summary"
  ],
  "field_types": {
    "grid_rows": "array<{row_id:string, idea:string, source_ref:string, evidence:string, contradiction:boolean}>  — au moins 1 ligne requise",
    "primary_cta": "object{label:string, action:string}",
    "answer_plan": "object{parts:array<string>, note?:string}",
    "contradiction_warning": "string — affiché quand une ligne contradiction=true est cochée"
  },
  "constraints": [
    "grid_rows doit contenir au moins 1 élément.",
    "Chaque ligne grid_rows porte les 4 champs obligatoires : row_id, idea, source_ref, evidence, contradiction (boolean).",
    "Le bouton CTA est verrouillé tant que toutes les lignes ne sont pas cochées (logique moteur).",
    "contradiction=true sur une ligne débloque le message contradiction_warning.",
    "answer_plan.parts : liste ordonnée des sections du plan ; jamais injectée depuis le moteur."
  ],
  "blocked_conditions": [
    "grid_rows absent ou vide",
    "primary_cta absent",
    "answer_plan absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "fallback_text"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, bouton verrouillé, 0 erreur" },
    { "case": "champ requis manquant (grid_rows vide)", "expected": "BLOCKED listant le champ" },
    { "case": "primary_cta absent", "expected": "BLOCKED listant le champ" },
    { "case": "toutes lignes cochées", "expected": "CTA débloqué, clic génère le plan depuis answer_plan" },
    { "case": "ligne contradiction=true cochée", "expected": "avertissement contradiction_warning affiché" },
    { "case": "instance externe injectée via init(ext)", "expected": "rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-328",
    "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)."
  }
}
