Решили: переименование сериализованного поля закрывается тремя проверками, а не одной: FormerlySerializedAs на самом поле, греп по СТРОКОВОМУ имени ("_visual") и греп по рефлексии (GetField("…")). Компилятор из этих трёх не ловит ни одну.

Почему: при переименовании UnitVisualAnimationArchetypeData и _visual_archetype компиляция прошла с первого раза, а прогон боевых тестов дал 123 падения из 536. Причина — EffectTestSupport ставил поле по строке (Set(r, "_visual", …)): строку компилятор не проверяет, и тест падал уже в рантайме, с честным «нет поля _visual в RelicData».

Грабли:

  • Вторая находка была страшнее и тест бы её НЕ поймал. RelicCardVisualRig доставал удалённое UnitView._sprite рефлексией: GetField вернул бы null, ветка молча пропускалась, и карточка реликвии свалилась бы на фолбэк «первый спрайт в иерархии» — рисуя вместо бойца случайную часть. Ни ошибки, ни падения; нашлось только грепом по GetField("_. Рефлексия по имени поля — это ссылка, которую не видит ни компилятор, ни тест.
  • FormerlySerializedAs обязателен и молчалив. Без него 47 юнит-ассетов потеряли бы архетип не сразу, а при первом сохранении каждого — то есть россыпью, по мере того как кто-то открывает ассеты. С ним ссылки живут: проверено сверкой guid на диске после переименования.
  • Переименование ассетов через AssetDatabase.RenameAsset guid сохраняет, и git видит это как rename — история файла не рвётся. Ручное переименование на диске при живом редакторе так не умеет.
  • Красный тест в общем дереве — не обязательно свой. Параллельно вскрылись два падения про battle_preset.menu_duel: ассет создан 04.08, а в ContentDatabase его нет ни в одном коммите — кто-то забыл прогнать синхронизацию базы. Отличить чужое от своего удалось только проверкой истории (git show <коммит>:<база> | grep <guid>), а не чтением сообщения теста.
  • Порядок «удалить → переименовать» имеет значение. Контроллер, который по имени числился боевым, стоял на дев-дуэлянте, а в бою играл «лабораторный». Удаление и переименование сделаны ОДНИМ шагом: между шагами дерево оказалось бы в состоянии, где имя и роль разошлись сильнее прежнего.

Владелец правды: UnitData._archetype (атрибут FormerlySerializedAs — он и есть договор о переименовании), EffectTestSupport, план tech/40-planning/weapon-system §6.1.