Решили: состав боевого скоупа выбирает роль сеанса. IBattleAuthority, NetBattleAuthority и BattleRole удалены целиком вместе с пятью ветвлениями, которые их спрашивали.

Почему это стало можно: обоснование интерфейса было честным и записано в его же докстринге — бой поднимался на буте, в главном меню, когда сети ещё нет, и решить роль регистрацией было нельзя. Причина отпала сегодняшним заходом: бой рождается по требованию внутри мероприятия, мероприятие — внутри сеанса, а роль сеанса выбрана в момент открытия и за его жизнь не меняется.

Соло отдельной ролью НЕ стало, хотя напрашивалось. Соло — это тот же владелец, просто без поднятого транспорта: разница между «играю один» и «играю хостом» вся в том, есть ли кому слать. Заведи мы третью роль — включение лобби посреди забега потребовало бы переоткрыть сеанс, то есть пересобрать состояние ради одного флага. Цена решения названа: у вещателя ленты осталась одна проверка, и она спрашивает соединение (IsRunning), а не роль.

Что развело классы. BattleRosterRelay делал обе стороны в одном теле и потому обязан был спрашивать роль. Разделён на BattleRosterAnnouncer (владелец объявляет спавны) и BattleRosterIntake (гость складывает паспорта). Тем же резом из CombatLoopService ушла гостевая ветка: у гостя теперь GuestPlaybackLoop — не «цикл с выключенным тиком», а другой класс на десять строк.

Роль спрашивается у предка, а не приезжает параметром боя: она свойство сеанса, бой её только читает. Parent.Container к моменту Configure уже заполнен — это проверено по исходнику VContainer 1.18 ещё в заходе про арену. Сеанса нет вовсе (одиночная боевая сцена) — собираем владельческий состав: там некому быть хостом и нечего принимать.

Что стало с тестами. «Гость не раздаёт» проверять больше негде и незачем: у гостя вещателя нет как объекта — это сильнее теста, ровно как «у гостя нет сервиса, пишущего сейв». Осталась вторая половина того же правила, которую составом не решить: соло не должен платить нарезкой чанков в пустоту, и она проверяется узлом с закрытым соединением.

Побочно закрылось: долг §3.22 (повторный бой у гостя не начинался, потому что сбросить приёмник и реестр было некому) сменил способ починки. Сброса больше не нужно вовсе — новый бой это новый скоуп, а приёмник и реестр рождаются вместе с ним.

Владелец правды: Game/CombatLifetimeScope.RegisterCoop, тесты CoopBattleWiringTests.