Решили: боевой скоуп закрывается scope.Dispose(), а не Object.Destroy(scope.gameObject). И мир не регистрирует сам себя — VContainer делает это за него.

Почему: проверка захода по исходнику VContainer 1.18 (не по памяти и не по сайту) нашла две вещи.

LifetimeScope.Dispose() — это DisposeCore() синхронно плюс Destroy(gameObject) следом. Через голый Destroy контейнер умирал только в конце кадра, а Close() + Open() рестарта происходят в одном вызове: умирающий бой отвязывал показ уже ПОСЛЕ того, как новый привязался, и гасил арену только что начавшегося боя. Защита «отвязываем только своё» это лечила, но лечила симптом; теперь порядок верен по построению, а защита осталась страховкой.

InstallTo последней строкой делает builder.RegisterInstance<LifetimeScope>(this).AsSelf() — то есть скоуп доступен инъекцией всегда. Наша ручная регистрация того же объекта была вторым владельцем факта на ровном месте.

Грабли: сайт VContainer про Dispose говорит («to unload a child scope, call Dispose()»), но не объясняет, чем это отличается от Destroy, — а отличие ровно в синхронности, и оно решает исход. Проверять надо исходник.

Что проверено и оказалось верным (записано, чтобы не проверять заново):

  • подписки боевых объектов на MessagePipe диспозятся своими Dispose; брокеры живут в Root, поэтому автоматической отписки «по концу скоупа» тут НЕ будет, и это единственная причина, по которой всё держится на дисциплине;
  • где подписка идёт на объект того же скоупа (BattleTapeDiagnostics → диспетчер и симуляция), отписка не нужна: издатель умирает вместе с подписчиком;
  • асинхронная петля симуляции гасится честно: AsyncStartableLoopItem.Dispose() зовёт cts.Cancel(), а сам диспатчер диспозится вместе с контейнером;
  • делегаты IBattleSession (часы, кнопка старта) отвязываются в DeploymentController.Dispose, а launch/reset/restart держит теперь мир, который вечен;
  • в боевом скоупе не осталось ни одного RegisterComponentInHierarchy, поэтому известная ловушка «MonoBehaviour со Scoped не уничтожается вместе со скоупом» нас не касается.

Владелец правды: Game/Flow/BattleHost.cs, Game/WorldLifetimeScope.cs.