пересмотрена архитектура под слои, свёртку в ответе и MCP

- агрегация появилась в ответе на запрос: род метрики измеряется сверкой
  слоёв, нижний слой HAE не суммируется никогда
- переведённые строки хранятся дословно с приписанным кодом HealthKit;
  снято правило «настройки данных у всех проходов одинаковы» — слой в ключе
- добавлены устаревание нижнего слоя по проверенному экспорту и MCP поверх
  read API; план вырос до 11 шагов
This commit is contained in:
av
2026-08-01 13:48:22 +03:00
parent 18d76d9610
commit 9d06509446
4 changed files with 363 additions and 95 deletions
+25 -15
View File
@@ -6,10 +6,12 @@
## Что это
Коллектор данных Apple Health. Принимает выгрузки из Health Auto Export,
хранит их и отдаёт другим моим проектам через HTTP API. Это **хранилище, а не
аналитика**: принять, дедуплицировать, сохранить, отдать. Не считать агрегаты,
не переименовывать поля Apple, не интерпретировать значения.
Коллектор данных Apple Health. Принимает выгрузки из Health Auto Export и
родного экспорта Apple, хранит их и отдаёт другим моим проектам через HTTP
API и через MCP. Это **хранилище, а не аналитика**: принять, дедуплицировать,
сохранить, отдать. Не переименовывать поля Apple, не интерпретировать
значения. Агрегат считается только в ответе на запрос и только там, где род
метрики измерен.
## Стек
@@ -27,18 +29,26 @@ Module path — `git.vakhrushev.me/av/healthlog`.
отбрасывать внутри точки — срок хранения архива станет сроком жизни данных.
- **Сохранили — значит приняли.** Код ответа отражает доставку, а не разбор:
битый JSON — 400, непонятое содержимое — 200.
- **Ничего не теряем молча.** Идентичность — хеш **канонизированного**
(рекурсивно отсортированного) содержимого: повтор не меняет ничего,
различие сохраняется. Изменение запечатанного часа — `WARN`, но данные
всё равно пишутся.
- **Дыры закрываются сами.** Три прохода синхронизации разной глубины
(5 минут / час / сутки), настройки данных у всех одинаковы.
- **Ничего не теряем молча.** Идентичность — координаты
(`метрика + слой + метка`); `source` в ключ не входит, он нестабилен. Хеш
канонизированного содержимого остался детектором изменений. При
столкновении выигрывает **более полная** точка, а не последняя. Изменение
запечатанного часа — `WARN`, но данные всё равно пишутся.
- **Дыры закрываются сами.** Три прохода разной глубины (5 минут / сутки /
неделя). Настройки данных у проходов теперь **разные** — намеренно, они
наполняют разные слои; это безопасно ровно потому, что слой входит в ключ.
- **Форма Apple не транслируется.** Значения отдаём как пришли, нормализовано
только время (`ts_utc` + офсет исходной зоны).
- **Своей агрегации нет — есть слои.** Метрика хранится в той подробности, в
какой пришла (`raw`/`minute`/`hour`); слой выводится из выравнивания меток,
а не из заголовка HAE — тот врёт. Клиенту показываем каталог разрезов, выбор
за ним.
только время (`ts_utc` + офсет исходной зоны). Единственное добавление —
стабильный код рядом с переведённой строкой: HAE отдаёт «БДГ» и «Сидячий
образ жизни» на языке телефона, а родной экспорт — коды HealthKit, и без
словаря эти два источника не сойтись.
- **Своей агрегации в хранении нет — есть слои.** Метрика лежит в той
подробности, в какой пришла (`sample`/`raw`/`minute`/`hour`); слой выводится
из выравнивания меток, а не из заголовка HAE — тот врёт.
- **Агрегация в ответе — только измеренная.** Род свёртки выводится сверкой
слоёв между собой (часовое = сумма минутных → накопительная, = среднее →
мгновенная), а не размечается руками. Род неизвестен — свёртки нет. И
никогда не суммируем нижний слой HAE: это интерполяция, а не сэмплы.
- **Секреты не в логах** — токены приёма и чтения. Данные о здоровье
чувствительны: тела запросов только на `DEBUG` и с обрезкой.