# Полнота точки — по множеству ключей, а не по их числу **Приоритет:** высокий Была блокером, вынутым ревью кода задачи `razbor-metrik-v-obekty` (профиль `deep`, находка №7 триажа, severity major). **Решение принято замером** (находка 49) — ниже задача на остаток. ## Что не так сегодня Полнота — это **счётчик** ключей, чьё значение не `null`, не `""` и не пусто. Поэтому `{}`, `[]`, `0` и `false` считаются содержательными: ``` точка {"qty":0,"a":0,"b":0,"c":{},"d":[]} полнота 5 точка {"date":"…","qty":123.4} полнота 2 ``` Вторая точка — настоящее измерение — проигрывает первой и стирается безвозвратно. Восстановить можно только из сырого архива, пока он жив. ## Что решено и на каком основании Замер по всем 99 доставкам (находка 49): настоящих столкновений 2 897 из 444 256 координат (0.65%), и среди них **ноль несравнимых наборов полей**. Отсюда: - **Полнота — сравнение множеств ключей, побеждает надмножество.** Ровно 981 случай из замера, и там правило уже работает верно — но работает случайно, через счётчик, который на другом входе даст обратное. - **Объединение полей при несравнимых наборах не нужно.** Самая дорогая часть обсуждавшегося варианта (1) на живом потоке не срабатывает ни разу. Вместо реализации — счётчик: несравнимый набор считается и логируется `WARN`. Если событие когда-нибудь наступит, оно будет видно, а не додумано заранее. - **Тай-брейк при равной полноте в эту задачу не входит.** Обоснование ниже. ## Что делать 1. `resolve` в `internal/store/bucket.go`: полнота — множество ключей с непустым значением; побеждает строгое надмножество. 2. Несравнимые множества — счётчик в `MergeStats` плюс `WARN` с координатами объекта (без значений). Точку выбирает тот же тай-брейк, что и при равной полноте. 3. Дельта-спека `openspec/specs/storage` → «Разрешение столкновений по полноте»: переформулировать с числа ключей на множество. 4. Тесты: «бедная точка с пятью пустыми полями не стирает измерение» (регрессия на приведённой выше паре), «надмножество побеждает», «несравнимые наборы считаются». 5. `task verify:archive` обязан дать то же состояние — правило меняет исход только там, где сегодня он неверен. ## Что отложено и почему **Тай-брейк при равной полноте.** Сегодня это лексикографический порядок канонических форм. Измерено (находка 49): в 1 847 случаях из 1 912 он выбирает **меньшее** значение — 96%. Четыре из шести затронутых метрик накопительные, там это систематический недосчёт; но крупнейшая группа, `heart_rate` (985 случаев), мгновенная, и там «большее» не правильнее. То есть правильный тай-брейк зависит от **рода метрики**, а род по замыслу проекта измеряется сверкой слоёв между собой. Выбирать его сейчас — угадывать ровно то, что станет известно точно. Зависимость: [rod-agregacii-i-katalog](rod-agregacii-i-katalog.md). Тай-брейк доделывается **после** неё, отдельной задачей. ## Что стоит без решения Ничего. Правило работает и **наблюдаемо**: столкновение считается расхождением канонических форм (а не байтов), даёт `WARN` с координатами объекта и счётчик `overwrites`.