# Тай-брейк при равной полноте точек - **Секция:** ядро - **Зачем:** При равной полноте порядок канонических форм берёт меньшее значение в 96% случаев — у накопительных это систематический недосчёт - **Теги:** goal:merge-robustness **Решение принято владельцем 2026-08-02: вариант (б) — брать бо́льшее значение точки.** Ниже — исходная постановка блокера, она же ТЗ; рекомендация в конце файла и есть выбранный вариант. Что важно не потерять при реализации: правило обязано остаться **тотальным** — числа у точки нет, значит откат на порядок канонических форм, — и обязано остаться полурешёткой: `max` коммутативен, ассоциативен и идемпотентен, поэтому воспроизводимость свёртки не страдает. Род агрегации в правило **не входит**: род есть функция витрины, и правило слияния, читающее собственную выдачу, повторяет дефект наследования слоя «из будущего» (`docs/review.md`, 2026-08-01). Приёмка та, что названа ниже: на прогоне живого архива отпечаток витрины обязан **измениться** (иначе правило не сработало), а число столкновений с равной полнотой — остаться прежним. ## Что происходит Какое правило выбирает победителя, когда по одним координатам приехали две точки с **равными** наборами содержательных полей и разными значениями. Структурная часть правила слияния закрыта (`pravilo-sliyaniya-tochek`); открыт только этот разряд. Сегодня это порядок канонических форм, и он измеримо смещён: из 1912 случаев, где сравнение чисел определено, лексикографический порядок берёт **меньшее** значение в 1847 — 96% (находка 49). Столкновений с равной полнотой 1916 из 444 256 координат, то есть 0.43% координат. ## Что стало известно Задача «Измеренный род агрегации и каталог разрезов» закрыла посылку, ради которой тай-брейк откладывали: род метрик теперь **измерен**, а не угадан (находка 53). Четыре из шести метрик, где тай-брейк системно берёт меньшее (`step_count`, `walking_running_distance`, `active_energy`, `basal_energy_burned`), измерены как **накопительные** — там «меньшее» это систематический недосчёт порядка 0.4% координат, ровно тот, что HAE досчитывает задним числом (находка 10). Самая крупная группа, `heart_rate`, измерена как **мгновенная**, и там выбор безразличен: это пересэмплирование, а не досчёт. И тем же измерением закрылся напрашивавшийся ответ: **сделать тай-брейк зависящим от измеренного рода нельзя**. Род есть функция витрины, витрина — результат слияния, и правило слияния, читающее собственную выдачу, повторяет ровно тот дефект, на котором свёртка уже переставала быть функцией префикса журнала (`docs/review.md`, 2026-08-01, наследование слоя «из будущего»). ## Варианты и цена **а. Оставить порядок канонических форм.** Цена: систематический недосчёт 0.4% координат у накопительных метрик, невидимый до сверки с родным экспортом Apple, то есть месяцами. Плюс: ноль работы, правило остаётся структурным и не знает ничего о значениях. **б. Брать бо́льшее значение точки.** Правильно для накопительных (досчёт растёт, находка 10, и набор полей у версий тренировки ни разу не уменьшался) и безвредно для мгновенных (пересэмплирование). Цена: слияние перестаёт быть структурным — оно начинает знать, какое поле точки несёт число (`hae.PointValue` уже есть). Метрика, у которой «большее» неверно, в потоке не наблюдалась, но и не исключена; правило приходится делать тотальным (нет числа — откат на порядок канонических форм), то есть в нём появляется вторая ветка. **в. Провенанс у точки и тай-брейк по позиции в журнале** — как у сущностей. Цена: колонка провенанса на точку (или на объект) и рост объёма нижнего слоя; плюс это не работает для столкновений **внутри одной доставки**, где `received_at` общий, а таких четверть (находка 47: 33 столкновения внутри доставки на эпизодах сна). То есть вариант не самодостаточен и всё равно требует второго разряда. ## Что заблокировано Ничего срочного: сегодняшнее правило детерминировано и воспроизводимо, витрина остаётся свёрткой журнала. Блокирован только сам недосчёт — он копится молча. Сверить его величину можно будет после `healthlog import`: родной экспорт Apple даст независимый эталон по тем же периодам. ## Рекомендация **Вариант б.** Он чинит измеренное смещение там, где оно есть, и не трогает там, где его нет; цена — одна ветка в правиле слияния и признание, что слияние знает про число точки (а оно уже знает — `hae.PointValue` живёт в разборе). От варианта «а» отличается тем, что перестаёт систематически терять данные; от «в» — тем, что не требует ни колонки, ни решения для внутридоставочных столкновений. Проверять на прогоне живого архива: отпечаток витрины обязан измениться (иначе правило не сработало), а число столкновений с равной полнотой — остаться прежним. Связано: `docs/architecture.md` → «Разрешение столкновений», находки 10, 47, 49, 53.