{
  "contract_version": "gab_345_contract_v1",
  "gab_id": "GAB-345",
  "canonical_name": "SafetyFallbackLearningEscalateToHuman",
  "module_owner": "EdTechSafetyFallbackLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "escalation_id",
    "escalation_threshold",
    "recipient",
    "consent"
  ],
  "optional_fields": [
    "title",
    "prompt",
    "message",
    "privacy",
    "follow_up_status",
    "states",
    "use_when",
    "do_not_use_when",
    "fallback_text_summary",
    "child_safety"
  ],
  "field_types": {
    "escalation_threshold": "string — phrase déclenchant l'escalade (situation qui dépasse l'outil)",
    "prompt": "string — invite à choisir le destinataire",
    "recipient": "array<{id,icon,label,dest}> — destinataires humains proposés (jamais inventer une identité/coordonnée réelle)",
    "message": "string — message minimal transmis, sans détail privé",
    "privacy": "string — mention de minimisation des données",
    "consent": "object{confirm_label,cancel_label,destination_template}",
    "follow_up_status": "object{sent,sent_button_label,cancelled}",
    "states": "object{initial,permission_check,sent,not_sent} — libellés d'état d'escalade",
    "use_when": "array<string>",
    "do_not_use_when": "array<string>",
    "child_safety": "object{consent_required,minimal_data,user_in_control,review_required}"
  },
  "constraints": [
    "Consentement EXPLICITE obligatoire avant tout envoi : le bouton de confirmation est structurel (HTML), son libellé vient du JSON.",
    "Minimisation des données : le message transmis ne contient aucun détail privé inutile (champ privacy explicite).",
    "L'utilisateur garde le contrôle : annuler n'envoie rien (follow_up_status.cancelled).",
    "Anti-invention : aucune identité, coordonnée ou destinataire réel n'est inventé — uniquement les destinataires fournis dans recipient.",
    "destination_template / follow_up_status acceptent les placeholders {dest},{message},{privacy} substitués par le moteur."
  ],
  "blocked_conditions": [
    "escalation_threshold absent",
    "recipient vide",
    "consent absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "fallback_text"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, 0 erreur" },
    { "case": "champ requis manquant", "expected": "BLOCKED listant le champ" },
    { "case": "choix destinataire", "expected": "état permission_check + récap message minimal, CTA consentement affiché" },
    { "case": "confirmation consentement", "expected": "état sent + suivi 'en attente de prise en charge', bouton désactivé" },
    { "case": "annulation", "expected": "état not_sent, rien envoyé, utilisateur garde le contrôle" },
    { "case": "instance externe injectée", "expected": "le rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-345",
    "note": "GAB SENSIBLE child-safety/RGPD. 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. ⚠️ Revue Boss requise avant prod."
  }
}
