Решили: команда InflictInjury несёт сид ролла отдельным полем; применитель роллит рану от него, а не из потока IRngService забега.

Почему: ступень раны зависит от занятых слотов, то есть от состояния, — значит каскад обязан считаться у применителя, и ролл вместе с ним. Но возьми применитель общий поток RNG забега, результат зависел бы от того, сколько раз этот поток дёрнули ДО команды: у хозяина и гостя порядок чужих действий разный, и раны разъехались бы молча — поймалось бы это только рассинхроном через десяток узлов. Сид в команде делает применение самодостаточным: лог воспроизводится один в один сам по себе.

Отвергнуто: роллить рану у публикующей стороны и слать готовый id (ступень тогда неизвестна — она следствие каскада по состоянию); дать применителю сам IRngService (см. выше).

Грабли: связь «павший юнит → слот гильдии» нигде не хранится и выводится из ПОРЯДКА СПАВНА: отряд ставится строго в порядке RunState.Guild, поэтому порядковый номер юнита среди team-0 и есть индекс «Сосуда». На том же инварианте уже стоит запись позиций расстановки в сейв. Две ловушки при подсчёте: нумеровать надо все team-0 подряд, включая живых (иначе после первой потери рана уедет соседу), и пропускать призванных (иначе скелет некроманта сдвинет нумерацию).

Отсутствие записи в реестре кооп-прав поймал гейт-тест PlayerCapabilityRegistryTests — новая команда без строки про право не проходит. Работает как задумано.

Владелец правды: InjuryLedger.cs, BattleStartup.FallenGuildIndices, тесты InjuryLedgerTests, docs/player-capability-registry.md.