Files
healthlog/docs/plan.md
T
av 5e2385ba6e добавлены документация проекта и каркас разработки
- README, CLAUDE.md, docs: назначение и границы, архитектура, конвенции, план
- docs/local-research.md — 36 находок по формату Health Auto Export, снятых на
  живых данных; документация приложения местами расходится с тем, что оно шлёт
- Taskfile, .golangci.yml, самодокументируемый config.example.toml
2026-08-01 12:37:03 +03:00

7.0 KiB
Raw Blame History

План

Живой документ: шаги отмечаем по мере готовности, отложенное копится внизу.

Ближайшая цель

Минимальное ядро, которое принимает и сохраняет пакет. Проверяем его локально: сервис поднят на рабочей машине, телефон шлёт на её IP по локальной сети. Ни деплоя, ни домена, ни TLS для этого не нужно — они понадобятся, когда сервис поедет на rivendell (шаг 8).

Это шаги 12.

Шаги

  • 1. Каркас. Taskfile.yml, .golangci.yml, CLAUDE.md, TOML-конфиг с валидацией на старте, логгер, подкоманда serve с /healthz.
  • 2. Приём без разбора. POST /api/v1/ingest: лимит тела, gzip, запись тела в архив, строка в delivery. Разбора ещё нет. ← подключаем телефон по локальной сети
  • 3. Разбор и хранилище. Миграции, часовые объекты метрик (bucket, ключ метрика + слой + час) + workout/record по своим id, вывод слоя из выравнивания меток, канонизация с округлением чисел и хеш содержимого, слияние точек в объект, два формата дат, healthlog reindex.
  • 4. Read API. Каталог метрик со слоями и диапазонами, точки с пагинацией и выбором слоя (сборка из часовых объектов), тренировки, записи.
  • 5. Самоописание. Каталог разрезов + выведенные из данных схемы содержимого со статистикой + статичная схема контракта API.
  • 6. healthlog import. Заливка полной истории кусками по годам.
  • 7. Наблюдаемость. /stats: последняя доставка, счётчики, тишина.
  • 8. Деплой. Dockerfile, сборка образа локально, доставка на rivendell, конфиг Caddy, поддомен, токены.

Порядок неслучаен. После шага 2 автоматизация в Health Auto Export включена и копит настоящие пакеты в сыром архиве. Документация формата HAE скудная, поэтому разбор на шаге 3 пишем по реальным данным, а не по догадкам — и заодно видим фактический объём и характер потока.

Отложено

  • Отсев идентичных тел доставок по sha256. Вычеркнуто: находка 2 показала, что порядок ключей в JSON нестабилен, поэтому два пакета с одними и теми же данными почти никогда не совпадают побайтно — хеш тела не сработает. Дедупликация возможна только по канонизированному содержимому, а это и делает хеш часового объекта. Отдельная механика не нужна.
  • Ретеншен сырого архива (storage.raw_retention, 14 дней) — удаление старых тел. Пока архив не подчищается; включить после того, как разбор устоится, иначе страховка исчезнет раньше, чем перестанет быть нужна.
  • Порог sealed — с какого возраста час считается запечатанным. Ставим по факту: сначала пишем WARN на изменение старых объектов и смотрим, какая глубина досчёта встречается в жизни (наблюдалось до 22 минут, находка 10).
  • Алерт «данных нет N часов». Тихо сломавшаяся автоматизация — главный эксплуатационный риск коллектора. В v1 факт виден в /stats; активное уведомление добавим после.
  • Аннотации к схемам — человеческие описания метрик поверх выведенных схем, либо рукописный каталог. Выбор зависит от того, насколько стабильным окажется формат; меняться он может только с обновлением Health Auto Export, а это отслеживается.
  • Схема тренировок — глубину вывода определим по факту, когда увидим, как приходят маршруты.
  • Пометка локализованных полей в схемах. context, value и подобные приходят на языке телефона и изменятся при смене языка iOS — клиентам не стоит завязываться на конкретные строки (local-research.md, находка 8).
  • Вторая автоматизация без группировки для sleep_analysis и heart_rate_variability — минутная группировка съедает фазы сна и межударные интервалы ценой ~130 точек в сутки (находка 6).
  • Переход на более грубый нижний слой. Посекундный слой стоит ~730 МБ в год против ~20 МБ у минутного. Когда решим, что мелкая подробность не нужна, достаточно выключить несуммированную автоматизацию — старые данные останутся в своих слоях, переписывать ничего не придётся. Обратный путь тоже есть: ручной экспорт из Apple Health + healthlog import восстанавливает нижний слой.
  • NDJSON-поток для больших выборок из read API.
  • Слой агрегаций поверх сырья (суточные/недельные срезы).
  • Разворачивание маршрутов тренировок в отдельную таблицу — если появится клиент, которому мало отдачи тренировки одним пакетом.