Решили: «спящих не выбирают целью» живёт в самом целеуказании (ProfileBrain.PickTarget, рядом с таким же правилом для скрытности), а не в эффекте сна. Исключение для того, кто спящих ищет намеренно, объявляется ДАННЫМИ: PreferTagged + TargetTag = Sleep.

Почему: сон трогает выбор цели у всех китов, включая ещё не написанных. Свойством эффекта это не выразить — эффект висит на цели, а решение принимает чужой мозг. Альтернатива «настроить каждого кита руками» отвергнута сразу: она разъезжается на первом же новом ките, и разъезд молчит. Исключение через данные, а не if (relic.Id == "relic.dreameater"), по той же причине: правило должно работать для любого будущего охотника за спящими.

Сон собран из существующего ControlComponent (запрет действий) плюс новый SleepComponent — только выход из сна. Разделение важно: «что запрещено» уже умеет система контроля, а «что рвёт сон» — свойство именно сна, и его цена (пробуждающий удар получает ×2) должна быть в одном месте.

Своя позиция в EffectTag понадобилась дважды: диспел по Control уносил бы заодно оглушения, а целеуказанию нужно отличать спящего от обездвиженного.

Грабли: нарастающий DoT считает разгон из ПРОЖИТОГО времени эффекта (BaseDuration минус остаток), а не из числа сработавших тиков — счётчик пришлось бы держать в RuntimeEffect, а компоненты stateless. Побочное следствие: подкрепление, обновившее длительность, сбрасывает разгон. Для «Кошмара» это верно по смыслу (сон начался заново), но для другого нарастающего эффекта может оказаться неожиданным — поэтому названо в тултипе поля.

Вторые грабли операционные: ассет с [SerializeReference] на НОВЫЙ тип, импортированный до того, как редактор скомпилировал этот тип, получает null в компонентах — и ловит это только ContentValidationTests. Причина была в отсутствующем .meta у нового .cs: без него редактор файл не видел вовсе. Лечится .meta + refresh + повторный ImportAsset(ForceUpdate) ассета.

Владелец правды: ProfileBrain.PickTarget/HuntsSleepers, SleepComponent.cs, PeriodicDamageComponent.Growth.