Решили: предбанник урона (IPreDamageComponent — щиты, негейт, уязвимости) больше не выполняется в момент заявки удара. Заявка кладёт в TickLedger сырой DamageRequest, а весь счёт идёт на коммите через новый шов ITickLedgerSink.ResolveIncoming. Заодно события развели на две очереди: заявления («каст объявлен») доставляются ДО коммита урона, последствия («по мне ударили», «я убил») — после.

Почему: предбанник не чист — он тратит запас щита эффекта, копит RuntimeUnit.AbsorbedByWard, жжёт заряды и (у парирования) дёргает RecastAttack носителя. Пока он срабатывал в момент заявки, первый заявленный удар менял мир посреди фазы решений, и боец, стоящий в обходе позже, решал по другому состоянию. Замер: два Антимага, обменявшись «Перегрузкой» в один тик, били 48 и 67.2 — второй успевал поглотить чужой удар вардом и обналичить его своей же «Перегрузкой». Комментарий в DealDamageCore утверждал обратное («расчёт чист и от порядка не зависит») — неверным было именно это утверждение, а не формула.

Отвергли: снимок накопителя на начало тика по образцу CanActAtTickStart / StacksAtTickStart — дешевле и лечит зеркало, но это правило ПЛЮС список полей-исключений: пятое поле заведут без снимка, и дефект вернётся молча. Выбор Макса — «самый честный вариант, чтобы самим не запутаться»: одно правило без исключений.

Грабли:

  • Порядок «заявления → коммит → последствия» — не деталь, а исполнение карточки the-aegis («щит встаёт до попадания»). До этого щит за вражеский каст приходил ПОСЛЕ того, как урон того же каста применён: заявление лежало в общей очереди последствий, а она дренится после коммита. Ловится AegisSliceTests.WardFromEnemyCast_AbsorbsPayloadOfThatSameCast — проверено, тест краснеет при прежнем порядке.
  • Порог значимости (MinSignificantAmount) переехал в коммит: до счёта неизвестно, что от удара останется после брони и щитов, а обрезать надо именно хвост отражения.
  • Покрытие зеркала держалось на удаче состава: Антимаг в ростере первый, поэтому в скользящее окно четвёрок попадает ровно один раз, а его дуэль зелёная лишь потому, что без чужой магии накопитель не наполняется. Сторож класса теперь наливает накопитель руками.
  • in hits[i].Req не компилируется (CS1612 — элемент списка не переменная): копировать элемент в локальную, потом передавать по in.

Владелец правды: TickLedger.cs (шов ResolveIncoming, DamageResolution), CombatSimulation.cs (_announceQueue, DrainAnnouncements, ResolveIncoming), тесты AegisSliceTests, MirrorAccumulatorTests, ParrySymmetryTests, MirrorMatchTests.