# GAB-204 · PlayEngineProviderBridge — « Bridge ref → module_owner »

**Archétype / renderer_key :** `text_cta` (cartographie) · **module :** EdTechPlayEngine
**Critère validé :** changer le JSON change le bridge (allowlist, owners_map, presets) sans modifier le HTML. ✅ check.py 12/12.

## Pack (structure officielle par-GAB)
```
GAB-204/
  renderer.html            ← moteur bridge (ne pas modifier par instance)
  instance.example.json    ← SOURCE DE VÉRITÉ (contenu réel, à plat)
  schema.contract.json     ← contrat de validation
  README-contract.md       ← ce fichier
```

## Archétype

**Bridge architectural PlayEngine** : composant de résolution `ref → module_owner`. PlayEngine reçoit une ref opaque (ex: `interactive/brevet-3e/revolution-cause-effect-lab`), la valide contre une allowlist, résout le module_owner responsable, vérifie les permissions (AccessGate/RLS), puis retourne le composant React cible. Ce gabarit ne possède PAS la logique pédagogique — il orchestre l'accès sans contourner les permissions.

## Champs requis (instance, à plat)
`gab_id` · `bridge_id` · `title` · `warning_text` · `flow_steps[]{num,title,detail,status,state}` · `allowlist_families[]` · `owners_map{}` · `test_presets[]{label,ref}`

Optionnels : `subtitle`, `flow_title`, `test_block_title`, `test_placeholder`, `test_default_ref`, `primary_cta{label,action}`.

## Ce qui vient du JSON vs HTML
- **JSON** : titre, sous-titre, texte d'avertissement sécurité, étapes du flow (titres, détails, statuts, états), allowlist des familles autorisées, mapping famille→module_owner, presets de test, libellé du bouton.
- **HTML** : layout du flow, zone de test interactive, coloration par état (ok/test/fail), logique de résolution, messages de feedback, rendu BLOCKED.

## Garde-fous (sécurité architecturale)
- **Anti-contournement** : allowlist_families est la source de vérité. Une famille absente = ref refusée, jamais acceptée.
- **Anti-injection** : refs HTTP (`http://`, `://`) bloquées avant tout parse.
- **Refs inexistantes** : family OK mais ref inconnue → fallback contrôlé affiché, erreur loggée (simulation `inexistant` dans le moteur).
- **BLOCKED** si `flow_steps` vide / `allowlist_families` vide / `owners_map` vide / `bridge_id` absent.

## QA à vérifier
1. Modifier `owners_map` → le module_owner résolu change sans toucher au HTML (critère d'or).
2. `allowlist_families:[]` → BLOCKED propre.
3. Ref famille non allowlistée → message de refus avec famille affichée.
4. Ref `http://...` → message injection bloquée.
5. Preset "ref inexistante" → family OK + fallback contrôlé.
6. Responsive 375/768/1024.

## external_refs / dependencies

Ce gabarit est **composite** : il fait référence à d'autres GABs du même lot.

| Référence | Nature |
|---|---|
| GAB-201 (PlayEngineCompositeSessionIntro) | même lot — session qui lance les blocs |
| GAB-202 (PlayEngineBlockStack) | même lot — pile de blocs orchestrée par PlayEngine |
| GAB-203 (PlayEngineMixedActivityStepper) | même lot — stepper multi-format |
| GAB-205 (PlayEngineSessionEndSummary) | même lot — fin de session |
| EdTechStoryLearning, EdTechVisualLearning, EdTechMemoryLearning, EdTechExerciseLearning, EdTechInteractiveLearning, EdTechGameLearning, EdTechAudioLearning, EdTechLevelTest, EdTechPathMap | modules cibles resolus par le bridge — hors lot |

## Source
`INDEX-300-playengine-composite-GAB-201-205-PLAYABLE.html` (stage `data-tpl="204"`, constantes `PB_ALLOWLIST`/`PB_OWNERS`, handlers `pbTest`/`pbPreset`).
