идентичность эпизода — интервал: блокер снят измерением

- находка 47: ключ (date,start,end) даёт 174 координаты сна против 170 по
  метке и ноль столкновений внутри доставки против 33; value в ключе лишний
- UUID в выгрузку Apple не попадает, другой идентичности у эпизода нет
- поэпизодный sleep_analysis вернулся в scope razbor-metrik-v-obekty
This commit is contained in:
av
2026-08-01 17:09:45 +03:00
parent 809153e03f
commit 3c9d226a34
11 changed files with 159 additions and 122 deletions
@@ -30,10 +30,6 @@
- Словарь категориальных значений: строка пока хранится дословно и без кода.
- Род агрегации и каталог разрезов.
- Своя агрегация при записи: слои не сводятся друг к другу никогда.
- **Хранение эпизодных схем** (поэпизодный `sleep_analysis`): модель их
идентичности вынесена блокером `identichnost-epizodnyh-metrik`. Точки
разбираются и считаются, но не сохраняются; тела в архиве, подберёт
пересборка.
## Decisions
@@ -125,6 +121,41 @@ double, и хеш-детектор начнёт видеть изменения
Сжатие наблюдалось около 25 раз — ~2 МБ в сутки вместо ~50 МБ.
### Эпизод адресуется интервалом, а не меткой
Координата `метрика + слой + метка` верна для точки-измерения и неверна для
точки-интервала: под одной меткой лежит до трёх разных эпизодов сна. Поэтому
у точки, несущей `start` и `end`, координата — `метрика + слой + start + end`.
Замер по всем 94 доставкам (1880 эпизодных точек, находка 47): метка одна даёт
170 координат и 33 столкновения **внутри одной доставки**, интервал — 174
координаты и ноль столкновений; `value` в ключе не добавляет ни одной
координаты. Из 174 координат ни одна не несёт двух разных содержимых, то есть
на эпизодном сне правило разрешения столкновений не срабатывает ни разу.
**Эпизодность выводится из данных, а не курируется списком** — так же, как слой
выводится из выравнивания меток, а не из заголовка. Признак: точка несёт `start`
и `end`, отличные от `date`. Список имён метрик здесь был бы вторым способом
описывать то, что уже сказано формой точки, и разошёлся бы с ней на первой же
новой метрике HAE.
Почему не «принять потерю и писать `WARN`»: в дублях внутри одной доставки
`received_at` общий, и тай-брейк по времени приёма неприменим в принципе —
исход решал бы порядок элементов в JSON-массиве, а он нестабилен (находка 2).
Свёртка перестала бы быть детерминированной: пересборка из архива давала бы не
то состояние, что живой приём.
Почему не append-only по хешу содержимого: это вторая модель идентичности в
`store` ради случая, которого можно избежать, и эпизоды не схлопывались бы
никогда — даже когда повтор действительно повтор, а их здесь 1706 из 1880.
Проверено против чужих решений (находка 47): единственная принятая схема
дедупликации Apple Health — `Start + End + тип`, без источника в ключе;
популярные ингесторы поверх InfluxDB ключуют по метке и теряют эпизоды
молчаливым last-write-wins движка. `HKObject.uuid` дал бы идентичность даром,
но **в выгрузку Apple он не попадает** — значит модель обязана выражаться через
`start`/`end`, иначе `import(экспорт)` не сойдётся с `replay(HAE)`.
### Слияние — по полноте, при равенстве — по `received_at`
Координатный ключ означает перезапись значения. Кто побеждает — решает