# Правило выбора победителя при столкновении точек **Приоритет:** блокеры Вынуто ревью кода задачи `razbor-metrik-v-obekty` (профиль `deep`, находка №7 триажа, severity major). Трогает записанный инвариант «при столкновении выигрывает более полная точка». ## Что решить Как выбирать победителя, когда по одним координатам приехали разные содержимые. Сегодняшнее правило измеримо неверно в двух местах. ## Оракул: измерено **Полнота — это счётчик ключей**, чьё значение не `null`, не `""` и не пусто. Поэтому `{}`, `[]`, `0` и `false` считаются содержательными: ``` точка {"qty":0,"a":0,"b":0,"c":{},"d":[]} полнота 5 точка {"date":"…","qty":123.4} полнота 2 ``` Вторая точка — настоящее измерение — проигрывает первой и стирается безвозвратно. Восстановить можно только из сырого архива, пока он жив. **Тай-брейк при равной полноте** — лексикографический порядок канонических форм, то есть исход зависит от самого значения. Для накопительной метрики, которую досчитывают задним числом, это систематическая победа **меньшего** числа: `qty:1` бьёт `qty:100`. Недосчёт, неотличимый от нормы. Частота на живом потоке **не измерена** — стоит прогнать по архиву до решения. ## Варианты и цена **(1) Полнота по множеству ключей: побеждает надмножество; несравнимые множества — объединять поля, а не выбирать точку целиком.** Цена: средняя — правка спеки, `resolve` и тестов. Самое честное прочтение инварианта «ничего не теряем молча»: при несравнимых наборах не теряется ничего вообще. **(2) Оставить счёт ключей, но не считать содержательными `{}`, `[]`, `0`, `false`, `""`.** Цена: малая. Правило остаётся играбельным (точку с лишними непустыми полями никто не мешает прислать), и произвол тай-брейка не решён. **(3) При равной полноте побеждает точка более поздней доставки по `received_at`.** Цена: малая, но спека должна признать зависимость от журнала, и нужен детерминированный порядок **внутри** одной доставки — а именно там измерены столкновения (31 координата в 21 доставке из 89). ## Рекомендация **(1).** Объединение полей при несравнимых наборах — единственный вариант, при котором столкновение вообще перестаёт быть выбором «кого потерять». ## Что стоит без решения Правило работает и теперь **наблюдаемо**: столкновение считается расхождением канонических форм (а не байтов, как было), даёт `WARN` с координатами объекта и счётчик `overwrites`. Если правило начнёт терять — это станет видно в логе, а не через месяцы при сверке с экспортом Apple. Связано: `openspec/specs/storage` → «Разрешение столкновений по полноте».