{
  "contract_version": "gab_187_contract_v1",
  "gab_id": "GAB-187",
  "canonical_name": "PathMapUnitSegment",
  "module_owner": "EdTechPathMap",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "segment_id",
    "title",
    "summary",
    "nodes"
  ],
  "optional_fields": [
    "mission_count",
    "objective",
    "progress",
    "primary_cta",
    "_note_dev"
  ],
  "field_types": {
    "title": "string — nom de la zone/segment",
    "summary": "string — sous-titre (ex: Segment N sur M · Nom du parcours)",
    "mission_count": "number — nombre total de missions dans le segment",
    "objective": "string — objectif pédagogique propre au segment",
    "progress": "object{completed:number, total:number, percent:number}",
    "nodes": "array<{num:number, title:string, mod_type:enum['story','interactive','exercise','visual','memory','game'], mod_label:string, mod_icon:string, duration:string, state:enum['completed','active','available','locked']}>",
    "primary_cta": "object{label:string, action:string}"
  },
  "constraints": [
    "nodes doit contenir au moins 1 entrée.",
    "nodes[].state doit être l'un de : completed, active, available, locked.",
    "nodes[].mod_type doit être l'un de : story, interactive, exercise, visual, memory, game.",
    "primary_cta.label : libellé venant du JSON, affiché tel quel sur le bouton.",
    "progress.percent dans [0,100] si présent ; calculable depuis completed/total."
  ],
  "blocked_conditions": [
    "title absent (BLOCKED)",
    "summary absent (BLOCKED)",
    "items (nodes) absents (BLOCKED)"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria_label_on_nodes",
    "locked_nodes_tabindex_minus1"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, 0 erreur" },
    { "case": "title absent", "expected": "BLOCKED listant le champ manquant" },
    { "case": "nodes vide ou absent", "expected": "BLOCKED listant items absents" },
    { "case": "node state=completed", "expected": "checkmark ✓ + style mint" },
    { "case": "node state=active", "expected": "numéro surligné violet + box-shadow violet" },
    { "case": "node state=locked", "expected": "opacité 0.6, tabindex=-1, cursor not-allowed" },
    { "case": "progress fourni", "expected": "barre remplie à percent% + label N/M · X%" },
    { "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" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-187",
    "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)."
  }
}
