Files
healthlog/docs/tasks/items/tie-break-equal-completeness.md
T
av d79189be18 docs: документация переведена на канон av-dev-pm
- беклог и план переехали в docs/tasks (38 задач, 11 целей), слаги
  переименованы с транслита на английские, 85 ссылок поправлены
- conventions.md разобран в docs/conventions/, local-research.md — в
  docs/research/, review-journal.md — в docs/review.md с разделом настройки
  конвейера; заведены security.md, adr/ и .pm.json
- шаг docs.py check добавлен в task gate; поведение в architecture.md помечено
  девятью маркерами долга, database.md получил настройки с числовым значением
2026-08-03 17:14:53 +03:00

92 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Тай-брейк при равной полноте точек
**Секция:** ядро · **Хук:** Решено: брать бо́льшее значение. Порядок канонических форм берёт меньшее в 96% случаев — для накопительных это систематический недосчёт · **Теги:** goal:merge-robustness, question
**Решение принято владельцем 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.