{
  "contract_version": "gab_301_contract_v1",
  "derived_from": "core_gab_schema_v1 :: GAB-301 input_contract",
  "gab_id": "GAB-301",
  "canonical_name": "DocumentLearningIntro",
  "module_owner": "EdTechDocumentLearning",
  "purpose": "Valide une instance d'intro d'activité documentaire. Renderer consomme l'instance validée; ne lit jamais le CORE-GAB.",
  "pipeline": "CORE-GAB.input_contract -> schema.contract.json -> valide instance -> renderer.html",

  "required_fields": ["gab_id","document_activity_id","title","document_ref","document_type","analysis_goal","primary_cta"],
  "optional_fields": ["estimated_duration","difficulty","source_notice","copyright_notice","fallback_text_summary"],

  "field_types": {
    "gab_id": "string ('GAB-301')",
    "document_activity_id": "string unique",
    "title": "string (titre pédagogique affiché)",
    "document_ref": "object { src:string, kind:string, alt:string }",
    "document_type": "enum['text','image','map','chart','table','photo','poster','letter','speech','extract','mixed']",
    "analysis_goal": "enum['observe','extract','interpret','compare','synthesize','answer_question','prepare_exam']",
    "primary_cta": "object { label:string, action:string }",
    "estimated_duration": "string",
    "difficulty": "enum['facile','moyen','difficile']",
    "source_notice": "string|null",
    "fallback_text_summary": "string (résumé texte si document indisponible)"
  },

  "constraints": [
    "document_type déclaré avant l'activité.",
    "primary_cta obligatoire (le bouton structurel reste dans le HTML, son LABEL vient du JSON).",
    "fallback_text_summary recommandé si document central."
  ],
  "blocked_conditions": ["document_ref absent","document_type absent","analysis_goal vide"],
  "edge_cases": [
    { "case": "document cassé", "expected": "afficher fallback_text_summary, jamais d'écran vide" },
    { "case": "source absente", "expected": "afficher avertissement 'source manquante'" }
  ]
}
