Files
EscapistsReintroduced/docs/guard-cancellation-fix.md

3.4 KiB

Retire initial population intent before purging

The first native guard-cancel test failed in logs_2026-09-19_07-27-00: at 07:27:50.658 it proved the empty automatic six-slot group had entered native initialization without dispatching a member, then stopped its pending owner twice. At 07:27:51.671 the group had received a later dispatch or member. The old purge-only implementation therefore did not establish cancellation for an incomplete group kept alive. The first log does not distinguish a later native expansion pass from an unobserved later request; the new fixture records owner cancellation calls, tagged EOnInit, RequestSpawn and ExpandOneMember calls while delegating every native method unchanged.

Installed SCR_AIGroup explains why a queued request is not the complete population contract:

  • EOnInit at line 2578 checks m_bSpawnImmediately, then requests the slot-array count.
  • RequestSpawn at 2684 enqueues into ChimeraAIWorld; its documentation describes later expansion to full size.
  • ExpandOneMember at 2725 returns without spawning when m_aUnitPrefabSlots is null/empty, and otherwise computes capacity from its count.
  • IsExpandComplete at 2771 treats null/empty configured slots as complete; this is native production behavior, not a fixture override.
  • SetSpawnImmediately at 2643 changes the per-instance automatic-initialization flag.
  • DespawnMembers at 2872 returns before its queue purge when no physical members exist.

For successor-owned PENDING initial preparation only, cancellation now disables automatic spawning, assigns a new empty prefab-slot array to that group instance, then purges its native queue requests. It never clears a possibly shared array, changes installed resources, invents dead counts, calls a spawn function, or suppresses ExpandOneMember. Existing partial members remain physical and owned until normal Rollback deletes them. READY gameplay groups remain excluded by the existing Stop state gate, so losses during ordinary play cannot cause this startup path to refill or rewrite their profile.

The unchanged core cancellation assertions still require zero further dispatches and stable cancelled members while an uncancelled control physically reaches six. Additional assertions verify that cancellation runs once, cancelled instance arrays become empty, and the control retains its six-slot profile and automatic-spawn setting. The test still deletes all disposable groups and actual characters through owner rollback and observes late activity.

test_guard_cancellation_policy.py executes the actual cancellation and Stop bodies with two cancelled groups sharing an authored six-slot array with an uncancelled control. It proves replacement does not mutate that shared array, existing physical actor counts stay unchanged, unrequested groups are skipped, READY owners do not retire profiles, and repeated Stop is idempotent. This is source-policy evidence only. The fix and diagnostics still require native compilation and another engine run; the failed first run remains a failure.

Native cancellation now passes in 073601: 68 assertions, no engine/script errors. Empty and partial groups remain stable through a fifteen-second observation while the uncancelled control fills. Owned rollback deletes all captured actors/groups, and the original mission groups remain unchanged.