{
  "contract_version": "gab_346_contract_v1",
  "gab_id": "GAB-346",
  "canonical_name": "SafetyFallbackLearningTechnicalError",
  "module_owner": "EdTechSafetyFallbackLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "error_id",
    "public_message",
    "retry_policy",
    "state_save"
  ],
  "optional_fields": [
    "state",
    "title",
    "primary_cta",
    "secondary_cta",
    "fallback_feedback",
    "admin_debug",
    "no_infinite_loop",
    "fallback_text_summary"
  ],
  "field_types": {
    "public_message": "string — message public utile, JAMAIS la stack technique",
    "retry_policy": "object{max_tries:number, retry_label, max_reached_label} — borne le nombre de tentatives (anti-boucle)",
    "state_save": "string — confirmation que l'état/travail est sauvegardé",
    "state": "enum['error_detected','retrying','max_retries','recovered']",
    "primary_cta": "object{label,action}",
    "secondary_cta": "object{label,action}",
    "admin_debug": "string — info debug réservée admin (séparée de l'élève)",
    "no_infinite_loop": "boolean"
  },
  "constraints": [
    "public_message : message utile à l'élève, interdiction d'afficher la stack technique.",
    "retry_policy.max_tries borne les tentatives — interdiction de réessayer à l'infini.",
    "Le travail doit être sauvegardé (state_save) avant toute reprise ou fallback.",
    "admin_debug reste côté admin, jamais montré comme contenu pédagogique à l'élève.",
    "CTAs : boutons structurels (HTML), libellés venant du JSON."
  ],
  "blocked_conditions": [
    "public_message absent",
    "retry_policy absent ou max_tries non numérique",
    "state_save absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "fallback_text"
  ],
  "child_safety": [
    "Aucune stack technique exposée à un mineur — message public uniquement.",
    "Pas de boucle infinie de réessais (limite explicite).",
    "État sauvegardé : aucune perte de travail, chemin de reprise sûr garanti."
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, état error_detected, 0 erreur" },
    { "case": "champ requis manquant", "expected": "BLOCKED listant le champ" },
    { "case": "retry jusqu'à max_tries", "expected": "passe à max_retries, bouton réessayer désactivé, pas de boucle" },
    { "case": "fallback choisi", "expected": "état recovered + message travail conservé" },
    { "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-346",
    "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)."
  }
}
