{
  "contract_version": "gab_310_contract_v1",
  "gab_id": "GAB-310",
  "canonical_name": "DocumentLearningDocumentSynthesis",
  "module_owner": "EdTechDocumentLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "synthesis_id",
    "ideas",
    "primary_cta"
  ],
  "optional_fields": [
    "title",
    "instruction",
    "feedback_all_ok",
    "feedback_offtopic",
    "feedback_missing",
    "fallback_text_summary"
  ],
  "field_types": {
    "synthesis_id": "string — identifiant unique de l'instance de dossier",
    "ideas": "array<{text:string, required:boolean}> — au moins 1 required:true et 1 required:false obligatoires pour que l'exercice soit discriminant",
    "primary_cta": "object{label:string, action:string}",
    "feedback_all_ok": "string — affiché quand toutes les idées required sont cochées et aucune non-required",
    "feedback_offtopic": "string — affiché quand au moins une idée non-required est cochée",
    "feedback_missing": "string — template avec {n} pour le nombre d'idées manquantes"
  },
  "constraints": [
    "ideas doit contenir au moins 1 item avec required:true et au moins 1 avec required:false.",
    "primary_cta : bouton structurel HTML, libellé venant du JSON.",
    "Un item required:false représente une idée hors-sujet à ne pas cocher.",
    "La synthèse = idées + preuves, pas un résumé linéaire — la distinction doit apparaître dans l'instruction ou les items."
  ],
  "blocked_conditions": [
    "ideas absent ou vide",
    "synthesis_id absent",
    "primary_cta absent",
    "aucun item required:true dans ideas"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "fallback_text"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, bouton actif, 0 erreur" },
    { "case": "champ requis manquant (ideas absent)", "expected": "BLOCKED listant le champ" },
    { "case": "synthesis_id absent", "expected": "BLOCKED listant le champ" },
    { "case": "primary_cta absent", "expected": "BLOCKED listant le champ" },
    { "case": "toutes required cochées + 0 non-required coché", "expected": "feedback_all_ok affiché (panel ok)" },
    { "case": "une idée non-required cochée", "expected": "feedback_offtopic affiché (panel warn)" },
    { "case": "idée(s) required manquante(s)", "expected": "feedback_missing avec n correct (panel warn)" },
    { "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" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-310",
    "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)."
  }
}
