7.8 KiB
Тай-брейк при равной полноте точек
Приоритет: высокий
Решение принято владельцем 2026-08-02: вариант (б) — брать бо́льшее значение точки. Ниже — исходная постановка блокера, она же ТЗ; рекомендация в конце файла и есть выбранный вариант.
Что важно не потерять при реализации: правило обязано остаться тотальным —
числа у точки нет, значит откат на порядок канонических форм, — и обязано
остаться полурешёткой: max коммутативен, ассоциативен и идемпотентен, поэтому
воспроизводимость свёртки не страдает. Род агрегации в правило не входит:
род есть функция витрины, и правило слияния, читающее собственную выдачу,
повторяет дефект наследования слоя «из будущего» (docs/review-journal.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-journal.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.