{
  "contract_version": "gab_204_contract_v1",
  "gab_id": "GAB-204",
  "canonical_name": "PlayEngineProviderBridge",
  "module_owner": "EdTechPlayEngine",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "bridge_id",
    "title",
    "warning_text",
    "flow_steps",
    "allowlist_families",
    "owners_map",
    "test_presets"
  ],
  "optional_fields": [
    "subtitle",
    "flow_title",
    "test_block_title",
    "test_placeholder",
    "test_default_ref",
    "primary_cta"
  ],
  "field_types": {
    "flow_steps": "array<{num:integer, title:string, detail:string, status:string, state:enum['ok','test','fail']}>",
    "allowlist_families": "array<string>  — chaque entrée se termine par '/'",
    "owners_map": "object<family_string, module_owner_string>  — clés = éléments de allowlist_families",
    "test_presets": "array<{label:string, ref:string}>",
    "primary_cta": "object{label:string, action:string}"
  },
  "constraints": [
    "allowlist_families : toutes les entrées doivent se terminer par '/'.",
    "owners_map : chaque clé doit être présente dans allowlist_families.",
    "flow_steps : champ state limité à 'ok', 'test', 'fail'.",
    "test_presets : au moins 1 preset doit produire une ref refusée (ex: famille non allowlistée ou injection HTTP).",
    "warning_text : texte de sécurité architectural — jamais vide, jamais inventé."
  ],
  "blocked_conditions": [
    "flow_steps absent ou vide",
    "allowlist_families absent ou vide",
    "owners_map absent ou vide",
    "bridge_id absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "fallback_text"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, flow affiché, test interactif opérationnel" },
    { "case": "champ requis manquant (bridge_id)", "expected": "BLOCKED listant le champ manquant" },
    { "case": "ref allowlistée valide", "expected": "résolution OK avec module_owner affiché" },
    { "case": "ref famille non allowlistée", "expected": "message 'Ref refusée · famille non allowlistée'" },
    { "case": "tentative injection HTTP (http://...)", "expected": "message 'Tentative d injection bloquée'" },
    { "case": "instance externe injectée via 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-204",
    "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)."
  }
}
