Решение принято в Фазе 1, точная дата не зафиксирована; запись извлечена из замороженного 20-explanation/di-events.

Решили: боевая сборка не знает про MessagePipe. Combat — нижний слой, наружу отдаёт голые C#-события (десяток: спавн и смерть юнита, урон, лечение, уклонение, начало и конец боя). В шину их ретранслирует CombatPresenter, уже в слое презентации — оттуда их слушают Audio, VFX и UI.

Почему: сборка, лежащая ниже всех, не должна тянуть за собой пакет ради развязки тех, кто выше. Голое C#-событие — это ноль зависимостей и ноль настройки, а подписчикам всё равно нужен слой, который знает и про сим, и про Unity. Заодно снят статический доступ к шине: провайдер GlobalMessagePipe удалён, потому что его никто не звал, а глобальная поверхность жила и приглашала ею воспользоваться.

Грабли: инвариант держится соглашением, а не тестом — проверяется тем, что using MessagePipe не встречается в Scripts/Combat ни разу. Первый, кто напишет там using, ничего не сломает и ничего не заметит. По нашему же правилу («инвариант между файлами идёт в тест») здесь нужен guard-тест на границу сборок; занесён в Meta - Tech Debt §5.6.

Владелец правды: Assets/_Project/Scripts/Combat/ (отсутствие зависимости) и Assets/_Project/Scripts/Presentation/CombatPresenter.cs (ретрансляция в шину); правило слоёв — CLAUDE.md.