extends SceneTree const PLAYER_CASH := &"PLAYER_CASH" var _checks := 0 var _failures := 0 var _clock: Variant var _state: Variant var _runtime: Variant var _command_sequence := 0 func _initialize() -> void: call_deferred("_run") func _run() -> void: print("S5: integrated Defense runtime cycle") _test_all_commands_multiwave_and_terminal_replay() _test_scheduled_command_time_alignment() _test_manual_retreat_cancel_and_long_horizon() _test_rng_skill_and_outcome_atomicity() _test_cross_raid_locks() _test_snapshot_replay_corruption_and_legacy_migration() _cleanup() if _failures == 0: print("PASS: %d checks" % _checks) else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks]) quit(_failures) func _reset_runtime(seed: int, hour := 6, minute := 0) -> void: _cleanup() _command_sequence = 0 _clock = load("res://autoload/game_clock.gd").new() _state = load("res://autoload/game_state.gd").new() _runtime = load("res://autoload/simulation_runtime.gd").new() _clock.reset(1, hour, minute) _state.reset() var report: Dictionary = _runtime.initialize(_clock, _state, seed) _clock.set_paused(true) _expect(report[&"valid"], "runtime initializes with the Defense aggregate") _expect( _runtime.defense_service.current_minute == _clock.absolute_minute, "Defense time begins aligned with SimulationKernel" ) _expect( _runtime.get_rng_stream(&"defense") != null and _runtime.get_rng_stream(&"defense").get_stream_id() == &"defense", "runtime owns a dedicated Defense RNG stream" ) func _cleanup() -> void: if _runtime != null: _runtime.free() if _clock != null: _clock.free() if _state != null: _state.free() _runtime = null _clock = null _state = null func _test_all_commands_multiwave_and_terminal_replay() -> void: _reset_runtime(7_101) _create_staff_and_facility(&"guard.multi", &"objective.multi", 94, 12, 9_750) var created := _submit(&"create_defense_raid", { &"raid_id": &"raid.multi", &"definition": _raid_definition(&"objective.multi", [ _wave(&"wave.alpha", 15, 1, [&"gate.north"]), _wave(&"wave.beta", 20, 1, [&"gate.south"]), ]), }) var defender := _submit(&"register_defense_combatant", { &"raid_id": &"raid.multi", &"actor_id": &"guard.multi", &"side": &"defender", &"combat": 90, &"max_health": 100, &"options": {&"armor": 3}, }) var attacker_alpha := _submit(&"register_defense_combatant", { &"raid_id": &"raid.multi", &"actor_id": &"raider.alpha", &"side": &"ATTACKER", &"combat": 15, &"max_health": 20, &"options": { &"wave_id": &"wave.alpha", &"entry_point_id": &"gate.north", }, }) var attacker_beta := _submit(&"register_defense_combatant", { &"raid_id": &"raid.multi", &"actor_id": &"raider.beta", &"side": &"ATTACKER", &"combat": 20, &"max_health": 20, &"options": { &"wave_id": &"wave.beta", &"entry_point_id": &"gate.south", }, }) var asset := _submit(&"register_defense_asset", { &"raid_id": &"raid.multi", &"asset_id": &"objective.multi", &"kind": &"objective", &"max_integrity": 10_000, &"defense": 40, &"options": {&"target_order": 1, &"armor": 2}, }) var policy := _submit(&"set_defense_retreat_policy", { &"raid_id": &"raid.multi", &"policy": &"threshold", &"threshold": 5, }) var started := _submit(&"start_defense_raid", {&"raid_id": &"raid.multi"}) var skill := _submit(&"queue_defense_skill", { &"raid_id": &"raid.multi", &"action_id": &"skill.multi.rally", &"actor_id": &"guard.multi", &"kind": &"rally", &"options": {&"potency": 7, &"metadata": {&"label": "free"}}, }) _expect( created.is_success() and defender.is_success() and attacker_alpha.is_success() and attacker_beta.is_success() and asset.is_success() and policy.is_success() and started.is_success() and skill.is_success(), "create, register, policy, start and skill Commands complete through Runtime" ) _expect( created.command_id > 0 and created.target_id == &"raid.multi" and defender.command_id > created.command_id, "Defense Command results preserve Runtime command_id and target conventions" ) var early := _submit(&"resolve_defense_round", { &"raid_id": &"raid.multi", &"resolution_id": &"round.multi.early", }) _expect(not early.is_success(), "a round before its configured interval is rejected") _advance(5) var first_round := _submit(&"resolve_defense_round", { &"raid_id": &"raid.multi", &"resolution_id": &"round.multi.1", }) var after_first: Dictionary = _runtime.defense_service.get_raid(&"raid.multi") _expect( first_round.is_success() and int(after_first[&"state"]) == DefenseService.RaidState.BETWEEN_WAVES and int(after_first[&"current_round"]) == 1, "first max-round Wave resolves and waits for an explicit transition" ) _expect( _runtime.facility_service.get_instance(&"objective.multi").durability == 9_750, "non-terminal round damage stays inside Defense until terminal reconciliation" ) var next_wave := _submit(&"begin_next_defense_wave", {&"raid_id": &"raid.multi"}) _expect( next_wave.is_success() and int(_runtime.defense_service.get_raid(&"raid.multi")[&"state"]) == DefenseService.RaidState.ACTIVE, "begin_next_defense_wave is the required explicit Wave transition" ) var same_minute := _submit(&"resolve_defense_round", { &"raid_id": &"raid.multi", &"resolution_id": &"round.multi.same-minute", }) _expect(not same_minute.is_success(), "a different round ID cannot resolve again in the same minute") _advance(5) var terminal := _submit(&"resolve_defense_round", { &"raid_id": &"raid.multi", &"resolution_id": &"round.multi.2", }) var terminal_raid: Dictionary = _runtime.defense_service.get_raid(&"raid.multi") var linked_staff: StaffRuntime = _runtime.staff_service.get_staff(&"guard.multi") var linked_facility: FacilityInstance = _runtime.facility_service.get_instance(&"objective.multi") _expect( terminal.is_success() and int(terminal_raid[&"state"]) == DefenseService.RaidState.DEFENDED, "second Wave reaches a terminal Raid result through resolve_defense_round" ) _expect( linked_staff.health == int(terminal_raid[&"combatants"][&"guard.multi"][&"health"]) and linked_staff.fatigue == int(terminal_raid[&"combatants"][&"guard.multi"][&"fatigue"]) and linked_staff.state == StaffRuntime.State.AVAILABLE, "terminal combatant outcome reconciles Staff health, fatigue and state" ) _expect( linked_facility.durability == int(terminal_raid[&"assets"][&"objective.multi"][&"integrity"]), "terminal asset outcome reconciles Facility durability" ) var rng_after: Dictionary = _runtime.get_rng_stream(&"defense").snapshot() var staff_after: Dictionary = _runtime.staff_service.create_snapshot() var facility_after: Dictionary = _runtime.facility_service.create_snapshot() var resolved_events := _count_domain_events(&"DEFENSE_RAID_RESOLVED", &"raid.multi") var replay := _submit(&"resolve_defense_round", { &"raid_id": &"raid.multi", &"resolution_id": &"round.multi.2", }) _expect(replay.is_success() and replay.is_replay, "duplicate round resolution replays its canonical result") _expect( _runtime.get_rng_stream(&"defense").snapshot() == rng_after and _runtime.staff_service.create_snapshot() == staff_after and _runtime.facility_service.create_snapshot() == facility_after, "resolution replay does not consume RNG or reapply external outcomes" ) _expect( _count_domain_events(&"DEFENSE_RAID_RESOLVED", &"raid.multi") == resolved_events, "resolution replay emits no duplicate Defense resolved event" ) var wrong_operation := _submit(&"retreat_defenders", { &"raid_id": &"raid.multi", &"resolution_id": &"round.multi.2", }) _expect(not wrong_operation.is_success(), "an explicit resolution ID cannot cross operation namespaces") _expect( _defense_terminal_event_order(&"raid.multi", &"round.multi.2"), "terminal Defense DomainEvents are structured and emitted in causal order" ) func _test_manual_retreat_cancel_and_long_horizon() -> void: _reset_runtime(7_202) _create_staff_and_facility(&"guard.retreat", &"objective.retreat") _prepare_started_raid( &"raid.retreat", &"guard.retreat", &"objective.retreat", [_wave(&"wave.retreat", 150, 8, [&"gate.north"])] ) var retreat := _submit(&"retreat_defenders", {&"raid_id": &"raid.retreat"}) _expect( retreat.is_success() and String(retreat.details.get(&"resolution_id", "")).begins_with("defense_retreat_resolution.") and int(_runtime.defense_service.get_raid(&"raid.retreat")[&"state"]) == DefenseService.RaidState.DEFENDERS_RETREATED, "manual retreat uses its stable operation-specific default resolution ID" ) _expect( _runtime.staff_service.get_staff(&"guard.retreat").state == StaffRuntime.State.FLEEING, "manual retreat projects the Defender state to Staff" ) _create_staff_and_facility(&"guard.cancel", &"objective.cancel", 100, 3, 9_900) _prepare_started_raid( &"raid.cancel", &"guard.cancel", &"objective.cancel", [_wave(&"wave.cancel", 600, 10, [&"gate.north"])], 25, 0, {&"target_order": 1} ) _advance(5) var partial := _submit(&"resolve_defense_round", { &"raid_id": &"raid.cancel", &"resolution_id": &"round.cancel.partial", }) var partial_raid: Dictionary = _runtime.defense_service.get_raid(&"raid.cancel") _expect( partial.is_success() and int(partial_raid[&"state"]) == DefenseService.RaidState.ACTIVE, "a long Wave can retain accumulated round state before cancellation" ) _expect( _runtime.facility_service.get_instance(&"objective.cancel").durability == 9_900, "accumulated active-Raid damage is not prematurely projected" ) var cancel := _submit(&"cancel_defense_raid", {&"raid_id": &"raid.cancel"}) var cancelled_raid: Dictionary = _runtime.defense_service.get_raid(&"raid.cancel") _expect( cancel.is_success() and String(cancel.details.get(&"resolution_id", "")).begins_with("defense_cancel_resolution.") and int(cancelled_raid[&"state"]) == DefenseService.RaidState.CANCELLED, "active Raid cancellation uses the terminal transaction boundary" ) _expect( _runtime.staff_service.get_staff(&"guard.cancel").health == int(cancelled_raid[&"combatants"][&"guard.cancel"][&"health"]) and _runtime.facility_service.get_instance(&"objective.cancel").durability == int(cancelled_raid[&"assets"][&"objective.cancel"][&"integrity"]), "cancel reconciles all damage accumulated in earlier rounds" ) _create_staff_and_facility(&"guard.long", &"objective.long") var long_definition := _raid_definition( &"objective.long", [_wave(&"wave.long", 20_000, 400, [&"gate.north"])], 5, 5 ) _prepare_started_raid( &"raid.long", &"guard.long", &"objective.long", long_definition[&"waves"], 1, 0, {}, 100, long_definition ) var long_rng_before: Dictionary = _runtime.get_rng_stream(&"defense").snapshot() _advance(1_440) var long_raid: Dictionary = _runtime.defense_service.get_raid(&"raid.long") _expect( _runtime.defense_service.current_minute == _clock.absolute_minute and int(long_raid[&"current_round"]) == 0 and int(long_raid[&"state"]) == DefenseService.RaidState.ACTIVE, "24-hour ticking advances Defense time without automatically resolving rounds" ) _expect( _runtime.get_rng_stream(&"defense").snapshot() == long_rng_before, "Defense ticking alone consumes no round RNG" ) var duration_terminal := _submit(&"resolve_defense_round", { &"raid_id": &"raid.long", &"resolution_id": &"round.long.duration", }) _expect( duration_terminal.is_success() and int(_runtime.defense_service.get_raid(&"raid.long")[&"state"]) == DefenseService.RaidState.DEFENDED and duration_terminal.details.get(&"reason", &"") == &"RAID_DURATION_EXHAUSTED", "the next explicit round applies max-duration terminal behavior" ) _create_staff_and_facility(&"guard.breach", &"objective.breach", 100, 0, 1) _prepare_started_raid( &"raid.breach", &"guard.breach", &"objective.breach", [_wave(&"wave.breach", 1_000, 1, [&"gate.north"])], 0, 0, {&"target_order": 1}, 1_000 ) _advance(5) var breached := _submit(&"resolve_defense_round", { &"raid_id": &"raid.breach", &"resolution_id": &"round.breach.terminal", }) _expect( breached.is_success() and int(_runtime.defense_service.get_raid(&"raid.breach")[&"state"]) == DefenseService.RaidState.BREACHED and _runtime.facility_service.get_instance(&"objective.breach").durability == 0 and _runtime.facility_service.get_instance(&"objective.breach").state == FacilityInstance.State.BROKEN, "destroyed Defense objective atomically projects Facility durability and state" ) func _test_scheduled_command_time_alignment() -> void: _reset_runtime(7_202) _create_staff_and_facility(&"guard.scheduled", &"objective.scheduled") _prepare_started_raid( &"raid.scheduled", &"guard.scheduled", &"objective.scheduled", [_wave(&"wave.scheduled", 1, 1, [&"gate.north"])], 100, 100, {&"target_order": 1}, 0 ) var execute_at: int = int(_clock.absolute_minute) + 5 var enqueued: CommandResult = _runtime.submit_command( &"resolve_defense_round", {&"raid_id": &"raid.scheduled", &"resolution_id": &"round.scheduled.1"}, &"defense_controller", &"raid.scheduled", 80, execute_at, &"runtime.defense.scheduled.round" ) _clock.set_paused(false) _clock.advance_minutes(4) _expect( enqueued.is_success() and _runtime.command_queue.get_processed_result(enqueued.command_id) == null and _runtime.defense_service.current_minute == execute_at - 1, "scheduled Defense round remains pending until its exact configured minute" ) _clock.advance_minutes(1) _clock.set_paused(true) var completed: CommandResult = _runtime.command_queue.get_processed_result(enqueued.command_id) _expect( completed != null and completed.is_success() and int(completed.details.get(&"absolute_minute", -1)) == execute_at and _clock.absolute_minute == execute_at and _runtime.defense_service.current_minute == execute_at, "scheduled Defense command resolves against the same absolute minute as the completed tick" ) var event_minutes: Dictionary = {} for event: SimulationDomainEvent in _runtime.domain_events.recent_events(): if &"raid.scheduled" not in event.entity_ids: continue if event.event_type in [ &"DEFENSE_ROUND_RESOLVED", &"DEFENSE_RAID_STATE_CHANGED", &"DEFENSE_RAID_RESOLVED", &"COMMAND_COMPLETED", ]: event_minutes[event.event_type] = event.absolute_minute _expect( event_minutes.size() == 4 and event_minutes.values().all(func(value: Variant) -> bool: return int(value) == execute_at), "scheduled Defense outcome and Command DomainEvents share the canonical execution minute" ) func _test_rng_skill_and_outcome_atomicity() -> void: _reset_runtime(7_303) _create_staff_and_facility(&"guard.atomic", &"objective.atomic", 100, 4, 9_800) _expect( bool(_runtime.inventory_service.add_stack( &"defense.inventory", &"mana_bomb", 5, 100, -1, _clock.absolute_minute, 1, &"stack.atomic" ).get(&"ok", false)), "skill-cost Inventory fixture is available" ) _prepare_started_raid( &"raid.atomic", &"guard.atomic", &"objective.atomic", [_wave(&"wave.atomic", 500, 1, [&"gate.north"])], 10, 0, {&"target_order": 1} ) var queued := _submit(&"queue_defense_skill", { &"raid_id": &"raid.atomic", &"actor_id": &"guard.atomic", &"kind": &"POWER_STRIKE", &"options": { &"potency": 10, &"metadata": {&"cost": { &"gold": 125, &"mana": 9, &"items": [{ &"inventory_id": &"defense.inventory", &"item_id": &"mana_bomb", &"quantity": 2, }], }}, }, }) _expect( queued.is_success() and String(queued.details.get(&"action_id", "")).begins_with("defense_skill_action."), "queued skill receives a stable generated action namespace" ) _advance(5) var defense_before: Dictionary = _runtime.defense_service.create_snapshot() var defense_rng_before: Dictionary = _runtime.get_rng_stream(&"defense").snapshot() var authority_rng_before: Dictionary = _runtime.get_rng_stream(&"authority").snapshot() var economy_before: Dictionary = _runtime.economy_service.create_snapshot() var state_before: Dictionary = _state.create_snapshot() var inventory_before: Dictionary = _runtime.inventory_service.create_snapshot() var staff_before: Dictionary = _runtime.staff_service.create_snapshot() var facilities_before: Dictionary = _runtime.facility_service.create_snapshot() var expected_rng := DeterministicRng.from_root_seed(0, &"defense") as DeterministicRng expected_rng.restore(defense_rng_before) var expected_defense_roll := expected_rng.randi_range(0, 100) var expected_attack_roll := expected_rng.randi_range(0, 100) var failure_switch := {&"armed": true} var runtime_outcome := Callable(_runtime, "_apply_defense_outcome_boundary") var failing_outcome := func(payload: Dictionary, phase: StringName, token: Variant) -> Dictionary: var report: Dictionary = runtime_outcome.call(payload, phase, token) if phase == &"apply" and bool(report.get(&"ok", false)) and bool(failure_switch[&"armed"]): failure_switch[&"armed"] = false report = report.duplicate(true) report[&"ok"] = false report[&"error"] = "Injected post-apply outcome failure." return report _runtime.defense_service.configure_callbacks( Callable(_runtime, "_roll_defense_round"), Callable(_runtime, "_apply_defense_skill_boundary"), failing_outcome, Callable(_runtime, "_apply_defense_rng_boundary") ) var failed := _submit(&"resolve_defense_round", { &"raid_id": &"raid.atomic", &"resolution_id": &"round.atomic.retry", }) _expect(not failed.is_success(), "an injected post-apply terminal failure rejects the round") _expect( _runtime.defense_service.create_snapshot() == defense_before and _runtime.get_rng_stream(&"defense").snapshot() == defense_rng_before and _runtime.get_rng_stream(&"authority").snapshot() == authority_rng_before, "failed outcome restores Defense state and its RNG without touching other streams" ) _expect( _runtime.economy_service.create_snapshot() == economy_before and _state.create_snapshot() == state_before and _runtime.inventory_service.create_snapshot() == inventory_before, "failed outcome restores Gold, Mana, item reservations and consumption" ) _expect( _runtime.staff_service.create_snapshot() == staff_before and _runtime.facility_service.create_snapshot() == facilities_before, "failed outcome restores Staff and Facility changes atomically" ) _expect( _count_domain_events(&"LEDGER_TRANSACTION_POSTED") == 0 and _count_domain_events(&"DEFENSE_RAID_RESOLVED", &"raid.atomic") == 0, "rolled-back skill and outcome effects leave no committed success events" ) _runtime._configure_defense_boundaries() var retried := _submit(&"resolve_defense_round", { &"raid_id": &"raid.atomic", &"resolution_id": &"round.atomic.retry", }) _expect( retried.is_success() and int(retried.details[&"defense_roll"]) == expected_defense_roll and int(retried.details[&"attack_roll"]) == expected_attack_roll, "retry consumes the exact rolls predicted before the failed transaction" ) _expect( _runtime.economy_service.get_balance(PLAYER_CASH) == 5_000 - 125 and _state.get_resource(&"gold") == 5_000 - 125 and _state.get_resource(&"mana") == 100 - 9 and _runtime.inventory_service.available_quantity( &"defense.inventory", &"mana_bomb", _clock.absolute_minute ) == 3, "successful retry commits explicit Gold, Mana and item costs once" ) _expect( _runtime.get_rng_stream(&"authority").snapshot() == authority_rng_before and _runtime.get_rng_stream(&"defense").snapshot() == expected_rng.snapshot(), "successful round advances only the Defense RNG to its exact predicted state" ) _reset_runtime(7_304) _create_staff_and_facility(&"guard.reserve", &"objective.reserve") _expect( bool(_runtime.inventory_service.add_stack( &"reserve.inventory", &"a_available", 1, 100, -1, _clock.absolute_minute, 1, &"stack.reserve.available" ).get(&"ok", false)), "partial-reservation rollback fixture is available" ) _prepare_started_raid( &"raid.reserve", &"guard.reserve", &"objective.reserve", [_wave(&"wave.reserve", 100, 1, [&"gate.north"])] ) var reservation_skill := _submit(&"queue_defense_skill", { &"raid_id": &"raid.reserve", &"action_id": &"skill.reserve.partial", &"actor_id": &"guard.reserve", &"kind": &"RALLY", &"options": {&"potency": 1, &"metadata": {&"cost": {&"items": [ {&"inventory_id": &"reserve.inventory", &"item_id": &"a_available", &"quantity": 1}, {&"inventory_id": &"reserve.inventory", &"item_id": &"z_missing", &"quantity": 1}, ]}}}, }) _expect(reservation_skill.is_success(), "multi-item skill cost queues before boundary validation") _advance(5) var reserve_defense_before: Dictionary = _runtime.defense_service.create_snapshot() var reserve_rng_before: Dictionary = _runtime.get_rng_stream(&"defense").snapshot() var reserve_inventory_before: Dictionary = _runtime.inventory_service.create_snapshot() var reserve_economy_before: Dictionary = _runtime.economy_service.create_snapshot() var reserve_state_before: Dictionary = _state.create_snapshot() var reservation_failed := _submit(&"resolve_defense_round", { &"raid_id": &"raid.reserve", &"resolution_id": &"round.reserve.failed", }) _expect(not reservation_failed.is_success(), "a later unavailable item rejects the whole skill-cost batch") _expect( _runtime.inventory_service.create_snapshot() == reserve_inventory_before and _runtime.economy_service.create_snapshot() == reserve_economy_before and _state.create_snapshot() == reserve_state_before, "partial item reservation failure rolls every external resource back" ) _expect( _runtime.defense_service.create_snapshot() == reserve_defense_before and _runtime.get_rng_stream(&"defense").snapshot() == reserve_rng_before, "skill validation failure leaves Defense and round RNG untouched" ) func _test_cross_raid_locks() -> void: _reset_runtime(7_404) _create_staff_and_facility(&"guard.lock", &"objective.lock") for raid_id: StringName in [&"raid.lock.a", &"raid.lock.b"]: var created := _submit(&"create_defense_raid", { &"raid_id": raid_id, &"definition": _raid_definition( &"objective.lock", [_wave(StringName("wave.%s" % raid_id), 50, 4, [&"gate.north"])] ), }) _expect(created.is_success(), "lock fixture Raid is created: %s" % raid_id) var mismatched_defender := _submit(&"register_defense_combatant", { &"raid_id": &"raid.lock.a", &"actor_id": &"guard.lock", &"side": &"DEFENDER", &"combat": 50, &"max_health": 100, &"options": {&"health": 99}, }) var mismatched_asset := _submit(&"register_defense_asset", { &"raid_id": &"raid.lock.a", &"asset_id": &"objective.lock", &"kind": &"OBJECTIVE", &"max_integrity": 9_999, &"defense": 20, &"options": {}, }) _expect( not mismatched_defender.is_success() and not mismatched_asset.is_success(), "registration rejects values that differ from Staff and Facility baselines" ) var first_defender := _register_defender(&"raid.lock.a", &"guard.lock", 50) var first_asset := _register_objective(&"raid.lock.a", &"objective.lock", 20) var second_defender := _register_defender(&"raid.lock.b", &"guard.lock", 50) var second_asset := _register_objective(&"raid.lock.b", &"objective.lock", 20) _expect(first_defender.is_success() and first_asset.is_success(), "first non-terminal Raid acquires Staff and Facility locks") _expect( not second_defender.is_success() and not second_asset.is_success(), "a second non-terminal Raid cannot reuse locked Defender or Facility IDs" ) var demolish := _submit(&"demolish_facility", {&"instance_id": &"objective.lock"}) _expect(not demolish.is_success(), "a Defense-locked Facility cannot be demolished") var cancelled := _submit(&"cancel_defense_raid", { &"raid_id": &"raid.lock.a", &"resolution_id": &"cancel.lock.release", }) _expect(cancelled.is_success(), "terminal cancellation releases derived Raid locks") second_defender = _register_defender(&"raid.lock.b", &"guard.lock", 50) second_asset = _register_objective(&"raid.lock.b", &"objective.lock", 20) _expect( second_defender.is_success() and second_asset.is_success(), "released locks are deterministically acquired by the remaining Raid" ) var snapshot: Dictionary = _runtime.create_snapshot() _expect(_runtime.restore_snapshot(snapshot), "snapshot restore rebuilds Defense locks without a mutable lock table") var health_corruption: Dictionary = snapshot.duplicate(true) var health_raids: Array = health_corruption[&"systems"][&"runtime"][&"defense"][&"raids"] for raw_raid: Variant in health_raids: if String(raw_raid.get(&"raid_id", "")) != "raid.lock.b": continue for raw_combatant: Variant in raw_raid[&"combatants"]: if String(raw_combatant.get(&"actor_id", "")) == "guard.lock": raw_combatant[&"health"] = 99 _expect( not _runtime.restore_snapshot(health_corruption) and _runtime.create_snapshot() == snapshot, "restore rejects a PREPARING Defender health baseline mismatch atomically" ) var integrity_corruption: Dictionary = snapshot.duplicate(true) var integrity_raids: Array = integrity_corruption[&"systems"][&"runtime"][&"defense"][&"raids"] for raw_raid: Variant in integrity_raids: if String(raw_raid.get(&"raid_id", "")) != "raid.lock.b": continue for raw_asset: Variant in raw_raid[&"assets"]: if String(raw_asset.get(&"asset_id", "")) == "objective.lock": raw_asset[&"integrity"] = 9_999 _expect( not _runtime.restore_snapshot(integrity_corruption) and _runtime.create_snapshot() == snapshot, "restore rejects a PREPARING Facility integrity baseline mismatch atomically" ) var duplicate_after_restore := _submit(&"create_defense_raid", { &"raid_id": &"raid.lock.c", &"definition": _raid_definition( &"objective.lock", [_wave(&"wave.lock.c", 30, 3, [&"gate.north"])] ), }) var locked_after_restore := _register_defender(&"raid.lock.c", &"guard.lock", 20) _expect( duplicate_after_restore.is_success() and not locked_after_restore.is_success(), "restored non-terminal ownership continues to enforce Staff exclusivity" ) var second_cancel := _submit(&"cancel_defense_raid", { &"raid_id": &"raid.lock.b", &"resolution_id": &"cancel.lock.second", }) _runtime.staff_service.get_staff(&"guard.lock").fatigue = 17 var repaired: CommandResult = _runtime.facility_service.set_operational_status( &"objective.lock", {&"durability": 9_850}, _clock.absolute_minute ) var reused_defender := _register_defender(&"raid.lock.c", &"guard.lock", 30) var reused_asset := _register_objective(&"raid.lock.c", &"objective.lock", 10) var evolved_snapshot: Dictionary = _runtime.create_snapshot() _expect( second_cancel.is_success() and repaired.is_success() and reused_defender.is_success() and reused_asset.is_success(), "terminal Raid history does not block later Staff reuse or Facility repair" ) _expect( _runtime.restore_snapshot(evolved_snapshot), "terminal outcomes remain historical while the newer live ownership restores" ) func _test_snapshot_replay_corruption_and_legacy_migration() -> void: _reset_runtime(7_505) _create_staff_and_facility(&"guard.save", &"objective.save", 97, 6, 9_600) _prepare_started_raid( &"raid.save", &"guard.save", &"objective.save", [_wave(&"wave.save", 2_000, 5, [&"gate.north"])], 80, 0, {&"target_order": 1} ) _advance(5) var first := _submit(&"resolve_defense_round", { &"raid_id": &"raid.save", &"resolution_id": &"round.save.1", }) _expect( first.is_success() and int(_runtime.defense_service.get_raid(&"raid.save")[&"state"]) == DefenseService.RaidState.ACTIVE, "mid-Raid fixture preserves an active Wave after its first round" ) var queued := _submit(&"queue_defense_skill", { &"raid_id": &"raid.save", &"action_id": &"skill.save.second", &"actor_id": &"guard.save", &"kind": &"FORTIFY", &"options": { &"potency": 8, &"target_id": &"objective.save", &"metadata": {&"cost": {&"gold": 0, &"mana": 0}}, }, }) _expect(queued.is_success(), "queued next-round skill is included in the mid-Raid snapshot") var origin: Dictionary = _runtime.create_snapshot() _advance(5) var first_branch := _submit_with_key(&"resolve_defense_round", { &"raid_id": &"raid.save", &"resolution_id": &"round.save.2", }, &"runtime.defense.save.resolve") var branch_snapshot: Dictionary = _runtime.create_snapshot() _expect(first_branch.is_success(), "first post-save Defense branch resolves") _expect(_runtime.restore_snapshot(origin), "mid-round Runtime snapshot restores atomically") _advance(5) var replay_branch := _submit_with_key(&"resolve_defense_round", { &"raid_id": &"raid.save", &"resolution_id": &"round.save.2", }, &"runtime.defense.save.resolve") _expect( replay_branch.is_success() and replay_branch.details == first_branch.details, "restored mid-round branch reproduces the exact Defense resolution" ) _expect( _runtime.create_snapshot() == branch_snapshot, "save/load reproduces Defense, RNG, costs, events and command state exactly" ) var retiring_service: DefenseService = _runtime.defense_service var detach_origin: Dictionary = _runtime.create_snapshot() _expect(_runtime.restore_snapshot(detach_origin), "same-state restore replaces the public Defense aggregate") var runtime_before_stale_mutation: Dictionary = _runtime.create_snapshot() var ghost_created := retiring_service.create_raid( &"raid.stale.ghost", retiring_service.current_minute, _raid_definition(&"objective.ghost", [_wave(&"wave.ghost", 10, 1, [&"gate.north"])]) ) retiring_service.advance_time(retiring_service.current_minute + 5) var stale_round := retiring_service.resolve_next_round( &"raid.save", &"round.save.stale", retiring_service.current_minute ) _expect( ghost_created.is_success() and stale_round.is_success() and _runtime.create_snapshot() == runtime_before_stale_mutation, "retired Defense references cannot consume Runtime boundaries or emit ghost events" ) var live_before_corruption: Dictionary = _runtime.create_snapshot() var missing_current_defense: Dictionary = live_before_corruption.duplicate(true) missing_current_defense[&"systems"][&"runtime"].erase(&"defense") missing_current_defense[&"systems"][&"runtime"][&"rng_streams"].erase("defense") _expect( not _runtime.restore_snapshot(missing_current_defense) and _runtime.create_snapshot() == live_before_corruption, "current Runtime format requires Defense state and rejects silent data loss" ) var corrupt_minute: Dictionary = live_before_corruption.duplicate(true) corrupt_minute[&"systems"][&"runtime"][&"defense"][&"current_minute"] = _clock.absolute_minute - 1 _expect( not _runtime.restore_snapshot(corrupt_minute) and _runtime.create_snapshot() == live_before_corruption, "corrupt Defense clock is rejected without changing any live Domain" ) var corrupt_rng_stream: Dictionary = live_before_corruption.duplicate(true) corrupt_rng_stream[&"systems"][&"runtime"][&"rng_streams"]["defense"]["stream_id"] = "authority" _expect( not _runtime.restore_snapshot(corrupt_rng_stream) and _runtime.create_snapshot() == live_before_corruption, "Defense RNG stream identity mismatch is rejected atomically" ) var corrupt_rng_seed: Dictionary = live_before_corruption.duplicate(true) corrupt_rng_seed[&"systems"][&"runtime"][&"rng_streams"]["defense"]["root_seed"] = 999_999 _expect( not _runtime.restore_snapshot(corrupt_rng_seed) and _runtime.create_snapshot() == live_before_corruption, "Defense RNG root-seed mismatch is rejected atomically" ) var corrupt_staff: Dictionary = live_before_corruption.duplicate(true) var saved_staff: Array = corrupt_staff[&"systems"][&"runtime"][&"staff"][&"staff"] for index: int in range(saved_staff.size()): if String(saved_staff[index].get(&"staff_id", "")) == "guard.save": saved_staff.remove_at(index) break _expect( not _runtime.restore_snapshot(corrupt_staff) and _runtime.create_snapshot() == live_before_corruption, "missing external Staff crosslink rejects the snapshot atomically" ) var corrupt_facility: Dictionary = live_before_corruption.duplicate(true) var saved_facilities: Array = corrupt_facility[&"systems"][&"runtime"][&"facilities"][&"instances"] for index: int in range(saved_facilities.size()): if String(saved_facilities[index].get(&"instance_id", "")) == "objective.save": saved_facilities.remove_at(index) break _expect( not _runtime.restore_snapshot(corrupt_facility) and _runtime.create_snapshot() == live_before_corruption, "missing external Facility crosslink rejects the snapshot atomically" ) _reset_runtime(7_606, 9, 17) var legacy: Dictionary = _runtime.create_snapshot() legacy[&"systems"][&"runtime"].erase(&"runtime_schema_version") legacy[&"systems"][&"runtime"].erase(&"kitchen_production") legacy[&"systems"][&"runtime"].erase(&"elevators") legacy[&"systems"][&"runtime"][&"path"][&"schema_version"] = 3 legacy[&"systems"][&"runtime"][&"path"].erase(&"vertical_walk_blockers") legacy[&"systems"][&"runtime"].erase(&"operations") legacy[&"systems"][&"runtime"].erase(&"defense") legacy[&"systems"][&"runtime"][&"rng_streams"].erase("defense") for infrastructure_key: StringName in [ &"excavation", &"spatial_subslots", &"route_projection", &"facility_infrastructure", ]: legacy[&"systems"][&"runtime"].erase(infrastructure_key) _expect(_runtime.restore_snapshot(legacy), "pre-Defense Runtime snapshot migrates without synthetic Raid state") _expect( _runtime.defense_service.get_raid_ids().is_empty() and _runtime.defense_service.current_minute == _clock.absolute_minute, "legacy migration installs an empty clock-aligned Defense aggregate" ) var migrated_rng: DeterministicRng = _runtime.get_rng_stream(&"defense") var expected_rng := DeterministicRng.from_root_seed(7_606, &"defense") as DeterministicRng _expect( migrated_rng.snapshot() == expected_rng.snapshot(), "legacy migration derives the missing Defense RNG from the saved world seed" ) var after_legacy: Dictionary = _runtime.create_snapshot() _expect( after_legacy[&"systems"][&"runtime"].has(&"defense") and after_legacy[&"systems"][&"runtime"][&"rng_streams"].has("defense"), "the next current-format snapshot persists migrated Defense state" ) func _create_staff_and_facility( staff_id: StringName, facility_id: StringName, health := 100, fatigue := 0, durability := 10_000 ) -> void: var staff := _submit(&"register_staff", { &"staff_id": staff_id, &"role_id": &"security", &"options": { &"state": StaffRuntime.State.AVAILABLE, &"health": health, &"fatigue": fatigue, &"skills": {&"combat": 100}, }, }) var facility: CommandResult = _runtime.facility_service.create_instance({ &"instance_id": facility_id, &"definition_id": &"defense_fixture", &"durability": durability, &"created_at": _clock.absolute_minute, }) _expect(staff.is_success(), "external Staff fixture is registered: %s" % staff_id) _expect(facility.is_success(), "external Facility fixture is registered: %s" % facility_id) func _prepare_started_raid( raid_id: StringName, staff_id: StringName, facility_id: StringName, waves: Array, defender_combat := 50, asset_defense := 20, asset_options: Dictionary = {}, attacker_combat := 0, definition_override: Dictionary = {} ) -> void: var definition := definition_override.duplicate(true) if not definition_override.is_empty() \ else _raid_definition(facility_id, waves) var created := _submit(&"create_defense_raid", { &"raid_id": raid_id, &"definition": definition, }) var defender := _register_defender(raid_id, staff_id, defender_combat) var asset := _register_objective(raid_id, facility_id, asset_defense, asset_options) var wave_index := 0 for wave_value: Variant in waves: var wave: Dictionary = wave_value var wave_entries: Array = wave.get(&"entry_points", [&"gate.north"]) var attacker := _submit(&"register_defense_combatant", { &"raid_id": raid_id, &"actor_id": StringName("attacker.%s.%d" % [raid_id, wave_index]), &"side": &"ATTACKER", &"combat": attacker_combat, &"max_health": 100, &"options": { &"wave_id": StringName(wave[&"wave_id"]), &"entry_point_id": StringName(wave_entries[0]), }, }) _expect(attacker.is_success(), "synthetic attacker is registered for %s" % String(wave[&"wave_id"])) wave_index += 1 var started := _submit(&"start_defense_raid", {&"raid_id": raid_id}) _expect( created.is_success() and defender.is_success() and asset.is_success() and started.is_success(), "Raid fixture starts with validated external links: %s" % raid_id ) func _register_defender(raid_id: StringName, staff_id: StringName, combat: int) -> CommandResult: return _submit(&"register_defense_combatant", { &"raid_id": raid_id, &"actor_id": staff_id, &"side": &"DEFENDER", &"combat": combat, &"max_health": 100, &"options": {&"armor": 2}, }) func _register_objective( raid_id: StringName, facility_id: StringName, defense: int, options: Dictionary = {} ) -> CommandResult: return _submit(&"register_defense_asset", { &"raid_id": raid_id, &"asset_id": facility_id, &"kind": &"OBJECTIVE", &"max_integrity": 10_000, &"defense": defense, &"options": options.duplicate(true), }) func _raid_definition( objective_id: StringName, waves: Array, round_minutes := 5, max_duration_minutes := 180 ) -> Dictionary: return { &"source_id": &"runtime_test", &"entry_points": [&"gate.north", &"gate.south"], &"objective_ids": [objective_id], &"waves": waves.duplicate(true), &"round_minutes": round_minutes, &"max_duration_minutes": max_duration_minutes, &"retreat_policy": &"HOLD", &"defender_retreat_threshold": 0, } func _wave( wave_id: StringName, threat_strength: int, max_rounds: int, entry_points: Array ) -> Dictionary: return { &"wave_id": wave_id, &"threat_strength": threat_strength, &"retreat_threshold": 0, &"max_rounds": max_rounds, &"entry_points": entry_points.duplicate(), } func _submit(command_type: StringName, payload: Dictionary) -> CommandResult: _command_sequence += 1 return _submit_with_key( command_type, payload, StringName("runtime.defense.%04d" % _command_sequence) ) func _submit_with_key( command_type: StringName, payload: Dictionary, idempotency_key: StringName ) -> CommandResult: return _runtime.submit_command( command_type, payload, &"defense_controller", StringName(payload.get(&"raid_id", payload.get("raid_id", ""))), 80, -1, idempotency_key ) func _advance(minutes: int) -> void: _clock.advance_minutes(minutes) _clock.set_paused(true) func _count_domain_events(event_type: StringName, entity_id: StringName = &"") -> int: var count := 0 for event: SimulationDomainEvent in _runtime.domain_events.recent_events(): if event.event_type != event_type: continue if entity_id.is_empty() or entity_id in event.entity_ids: count += 1 return count func _defense_terminal_event_order(raid_id: StringName, resolution_id: StringName) -> bool: var ordered_types: Array[StringName] = [] for event: SimulationDomainEvent in _runtime.domain_events.recent_events(): if raid_id not in event.entity_ids: continue if event.event_type in [ &"DEFENSE_SKILL_APPLIED", &"DEFENSE_ROUND_RESOLVED", &"DEFENSE_RAID_STATE_CHANGED", &"DEFENSE_RAID_RESOLVED", &"COMMAND_COMPLETED", ]: if event.event_type == &"DEFENSE_ROUND_RESOLVED" \ and event.payload.get(&"resolution_id", &"") != resolution_id: continue ordered_types.append(event.event_type) var round_index := ordered_types.rfind(&"DEFENSE_ROUND_RESOLVED") var state_index := ordered_types.rfind(&"DEFENSE_RAID_STATE_CHANGED") var resolved_index := ordered_types.rfind(&"DEFENSE_RAID_RESOLVED") var command_index := ordered_types.rfind(&"COMMAND_COMPLETED") return round_index >= 0 and state_index > round_index \ and resolved_index > state_index and command_index > resolved_index func _expect(condition: bool, label: String) -> void: _checks += 1 if condition: print(" ✓ %s" % label) return _failures += 1 push_error(" ✗ %s" % label)