{
  "status": "design_fixtures_not_publishable_game_data",
  "date": "2026-09-21",
  "plan": "../plan-boss-phases-mouvements.md",
  "instructions": [
    "Ces fixtures décrivent le contrat proposé du moteur pur, pas des fiches de monstre complètes du schéma 76.",
    "Engagement à t=0, aucune attaque en cours, aucune attente physique, sauf indication contraire.",
    "Les événements health_ratio sont des entrées de simulation du résolveur après dégâts/soins acceptés ; ils ne prescrivent pas de contourner le bouclier en jeu.",
    "Les attentes min_active_seconds et les activations mémorisées restent appliquées.",
    "derive_from, append_phases et prepend_tracks construisent une fixture indépendante en mémoire ; ce ne sont pas des champs du maître."
  ],
  "fixtures": {
    "boss_level_1": {
      "enabled": true,
      "kind": "boss",
      "start": "first_aggro",
      "engagement": "until_encounter_end",
      "default_phase": "recharge",
      "phases": [
        {"id": "shield", "display_name": "Carapace", "min_active_seconds": 0, "overrides": {"defense": {"invulnerable": true, "super_armor": true}}},
        {"id": "recharge", "display_name": "Recharge", "min_active_seconds": 3, "overrides": {"defense": {"invulnerable": false, "damage_taken_multiplier": 1.5}, "attacks": [], "direct_hit": false, "aggro_movement": "static"}},
        {"id": "laser", "display_name": "Balayage latéral", "min_active_seconds": 0, "overrides": {"aggro_movement": "right_rail", "attack_needs_sight": false}}
      ],
      "tracks": [
        {
          "id": "half_health", "label": "Sous 50 %", "enabled": true,
          "conditions": [{"path": "encounter.health_ratio", "operator": "<=", "value": 0.5}],
          "activation": "latched", "clock": "sequence", "on_preempted": "pause",
          "transition": {"mode": "finish_attack", "max_wait_seconds": 0.75, "urgent": false},
          "segments": [{"id": "beam_hold", "phase": "laser", "start": 0, "end": 8}],
          "after_end": "hold_last"
        },
        {
          "id": "shell_cycle", "label": "Cycle de carapace", "enabled": true,
          "conditions": [], "activation": "latched", "clock": "sequence", "on_preempted": "pause",
          "transition": {"mode": "interrupt", "max_wait_seconds": 0, "urgent": false},
          "segments": [{"id": "shell", "phase": "shield", "start": 0, "end": 6}, {"id": "opening", "phase": "recharge", "start": 6, "end": 9}],
          "loop": {"start": 0, "end": 9, "iterations": null},
          "after_end": "hold_last"
        }
      ]
    },
    "boss_level_2": {
      "derive_from": "boss_level_1",
      "append_phases": [{"id": "ultimate", "display_name": "Dernier recours", "min_active_seconds": 0, "overrides": {"speed": 180}}],
      "prepend_tracks": [
        {
          "id": "critical_health", "label": "Sous 20 %", "enabled": true,
          "conditions": [{"path": "encounter.health_ratio", "operator": "<=", "value": 0.2}],
          "activation": "latched", "clock": "sequence", "on_preempted": "pause",
          "transition": {"mode": "finish_attack", "max_wait_seconds": 0.75, "urgent": false},
          "segments": [{"id": "ultimate_hold", "phase": "ultimate", "start": 0, "end": 8}],
          "after_end": "hold_last"
        }
      ]
    },
    "finite_loop": {
      "enabled": true, "kind": "boss", "start": "first_aggro", "engagement": "until_encounter_end", "default_phase": "prepare",
      "phases": [
        {"id": "prepare", "min_active_seconds": 0, "overrides": {}},
        {"id": "shield", "min_active_seconds": 0, "overrides": {}},
        {"id": "recharge", "min_active_seconds": 0, "overrides": {}},
        {"id": "ultimate", "min_active_seconds": 0, "overrides": {}}
      ],
      "tracks": [
        {
          "id": "finite", "label": "Trois tours puis finale", "enabled": true,
          "conditions": [], "activation": "latched", "clock": "sequence", "on_preempted": "pause",
          "transition": {"mode": "interrupt", "max_wait_seconds": 0, "urgent": false},
          "segments": [
            {"id": "intro", "phase": "prepare", "start": 0, "end": 2},
            {"id": "shell", "phase": "shield", "start": 2, "end": 8},
            {"id": "opening", "phase": "recharge", "start": 8, "end": 11},
            {"id": "finale", "phase": "ultimate", "start": 11, "end": 15}
          ],
          "loop": {"start": 2, "end": 11, "iterations": 3}, "after_end": "hold_last"
        }
      ]
    },
    "temporary_mask": {
      "enabled": true, "kind": "elite", "start": "first_aggro", "engagement": "perception", "default_phase": "idle",
      "phases": [
        {"id": "idle", "min_active_seconds": 0, "overrides": {}},
        {"id": "shield", "min_active_seconds": 0, "overrides": {}},
        {"id": "recharge", "min_active_seconds": 0, "overrides": {}},
        {"id": "burst", "min_active_seconds": 0, "overrides": {}}
      ],
      "tracks": [
        {
          "id": "upper", "label": "Priorité temporaire", "enabled": true,
          "conditions": [], "activation": "latched", "clock": "combat", "on_preempted": "continue",
          "transition": {"mode": "interrupt", "max_wait_seconds": 0, "urgent": false},
          "segments": [{"id": "burst_window", "phase": "burst", "start": 4, "end": 6}], "after_end": "release"
        },
        {
          "id": "lower", "label": "Cycle inférieur", "enabled": true,
          "conditions": [], "activation": "latched", "clock": "sequence", "on_preempted": "pause",
          "transition": {"mode": "interrupt", "max_wait_seconds": 0, "urgent": false},
          "segments": [{"id": "shell", "phase": "shield", "start": 0, "end": 6}, {"id": "opening", "phase": "recharge", "start": 6, "end": 9}],
          "loop": {"start": 0, "end": 9, "iterations": null}, "after_end": "hold_last"
        }
      ]
    },
    "relative_gap": {
      "enabled": true, "kind": "elite", "start": "first_aggro", "engagement": "perception", "default_phase": "idle",
      "phases": [{"id": "idle", "min_active_seconds": 0, "overrides": {}}, {"id": "burst", "min_active_seconds": 0, "overrides": {}}],
      "tracks": [
        {
          "id": "delayed", "label": "Trou avant le geste", "enabled": true,
          "conditions": [], "activation": "latched", "clock": "sequence", "on_preempted": "pause",
          "transition": {"mode": "interrupt", "max_wait_seconds": 0, "urgent": false},
          "segments": [{"id": "late", "phase": "burst", "start": 2, "end": 4}], "after_end": "release"
        }
      ]
    }
  },
  "scenarios": [
    {
      "id": "initial_selection_before_fallback_entry", "fixture": "boss_level_1", "events": [],
      "expect": [{"t": 0, "active_phase": "shield", "active_track": "shell_cycle"}],
      "invariants": ["Ne pas jouer recharge puis attendre ses 3 s avant la première carapace."]
    },
    {
      "id": "infinite_cycle_boundaries", "fixture": "boss_level_1", "events": [],
      "expect": [
        {"t": 0, "active_phase": "shield"}, {"t": 5.999, "active_phase": "shield"},
        {"t": 6, "active_phase": "recharge"}, {"t": 8.999, "active_phase": "recharge"},
        {"t": 9, "active_phase": "shield"}, {"t": 15, "active_phase": "recharge"}, {"t": 18, "active_phase": "shield"}
      ]
    },
    {
      "id": "threshold_preserves_opening", "fixture": "boss_level_1",
      "events": [{"t": 7, "health_ratio": 0.48}],
      "expect": [
        {"t": 7, "active_phase": "recharge", "requested_phase": "laser", "wait_reason": "minimum_exposure"},
        {"t": 8.99, "active_phase": "recharge"}, {"t": 9, "active_phase": "laser"},
        {"t": 17, "active_phase": "laser", "holding": true}, {"t": 200, "active_phase": "laser", "holding": true}
      ],
      "invariants": ["L'entrée laser est émise une fois à t=9, pas à t=17 ou t=200.", "Son premier segment reçoit bien 8 s après l'application de la phase."]
    },
    {
      "id": "simultaneous_thresholds_priority", "fixture": "boss_level_2",
      "events": [{"t": 0, "health_ratio": 0.6}, {"t": 6.5, "health_ratio": 0.18}, {"t": 10, "health_ratio": 0.7}],
      "expect": [
        {"t": 6.5, "active_phase": "recharge", "requested_phase": "ultimate"},
        {"t": 9, "active_phase": "ultimate"}, {"t": 10, "active_phase": "ultimate"}, {"t": 100, "active_phase": "ultimate"}
      ],
      "invariants": ["Aucune entrée laser intermédiaire.", "Les phases et pistes du niveau 1 ne changent pas.", "Le soin ne désarme pas une ligne latched."]
    },
    {
      "id": "death_precedes_phase", "fixture": "boss_level_2",
      "events": [{"t": 6.5, "health_ratio": 0}],
      "expect": [{"t": 6.5, "encounter_state": "dead", "requested_phase": null}],
      "invariants": ["Aucune entrée de laser ou ultime.", "Un seul événement de mort, butin et compteur, même si un second callback arrive au même tick."]
    },
    {
      "id": "new_threshold_after_visual_end", "fixture": "boss_level_2",
      "events": [{"t": 7, "health_ratio": 0.48}, {"t": 100, "health_ratio": 0.19}],
      "expect": [{"t": 99, "active_phase": "laser", "holding": true}, {"t": 100, "active_phase": "ultimate"}],
      "invariants": ["Les conditions continuent après l'horizon de la frise et après hold_last."]
    },
    {
      "id": "finite_loop_suffix", "fixture": "finite_loop", "events": [],
      "expect": [
        {"t": 0, "active_phase": "prepare"}, {"t": 2, "active_phase": "shield"},
        {"t": 8, "active_phase": "recharge"}, {"t": 11, "active_phase": "shield"},
        {"t": 17, "active_phase": "recharge"}, {"t": 20, "active_phase": "shield"},
        {"t": 26, "active_phase": "recharge"}, {"t": 29, "active_phase": "ultimate"},
        {"t": 33, "active_phase": "ultimate", "holding": true}
      ]
    },
    {
      "id": "pause_while_preempted", "fixture": "temporary_mask", "events": [],
      "expect": [
        {"t": 3.999, "active_phase": "shield"}, {"t": 4, "active_phase": "burst"},
        {"t": 6, "active_phase": "shield", "lower_row_elapsed": 4},
        {"t": 8, "active_phase": "recharge"}
      ]
    },
    {
      "id": "continue_while_preempted", "fixture": "temporary_mask",
      "fixture_changes": [{"track_id": "lower", "field": "on_preempted", "value": "continue"}],
      "events": [],
      "expect": [{"t": 4, "active_phase": "burst"}, {"t": 6, "active_phase": "recharge", "lower_row_elapsed": 6}, {"t": 9, "active_phase": "shield"}]
    },
    {
      "id": "gap_does_not_freeze_clock", "fixture": "relative_gap", "events": [],
      "expect": [{"t": 0, "active_phase": "idle"}, {"t": 1.99, "active_phase": "idle"}, {"t": 2, "active_phase": "burst"}, {"t": 4, "active_phase": "idle"}]
    }
  ],
  "integration_scenarios": [
    {
      "id": "right_rail_laser",
      "setup": "Arène dégagée, pattern right_rail, attaque rectangulaire ancrée acteur, target_mode forward, attack_facing keep, free_movement true, attack_needs_sight false.",
      "expect": ["Déplacement continu dans les deux sens verticaux.", "Regard et tirs à gauche même si le joueur passe derrière.", "Avertissement et dégâts superposés.", "Aucune pause d'une frame à chaque départ de tir.", "Pas de téléportation à l'entrée du trajet ni après un recul."]
    },
    {
      "id": "shield_composition",
      "setup": "Passer entre carapace et recharge pendant que des attaques possèdent ou non leurs propres i-frames.",
      "expect": ["Finir une attaque ne supprime pas le bouclier de phase.", "La phase recharge ne supprime pas les i-frames d'un geste.", "Les DOT positifs sont bloqués par une carapace ; les soins suivent leur branche propre.", "Aucun son ou effet de blessure sur des dégâts refusés."]
    },
    {
      "id": "snapshot_and_cooldown",
      "setup": "Tirer le geste A, changer de phase et réordonner la liste avant l'impact du projectile.",
      "expect": ["Projectile A conserve ses dégâts et sons d'origine.", "Le cooldown reste attaché à l'ID A.", "Le changement de phase ne remet pas à zéro PV, effets, taille, cooldowns ou compteurs."]
    },
    {
      "id": "minimum_then_attack_wait",
      "setup": "Une phase impose 3 s d'exposition, une nouvelle ligne finish_attack est demandée à sa première seconde ; max_wait_seconds=0.75.",
      "expect": ["L'attente de 0.75 s ne court qu'après satisfaction des 3 s minimales.", "Aucune nouvelle attaque n'est admise pendant la demande en attente.", "Une attaque encore bloquante est annulée au plus tard après les 0.75 s supplémentaires.", "La mort reste immédiate et ne dépend pas de ces attentes."]
    }
  ]
}
