Files
healthlog/openspec/changes/archive/2026-08-01-razbor-metrik-v-obekty/proposal.md
T
av 37413bb551 change razbor-metrik-v-obekty заархивирован
Дельты влиты в openspec/specs (parsing, storage), задача убрана из беклога,
план отражает сделанную часть шага 3.

Не закрыт один пункт: живая доставка с телефона не разобрана — поток молчит
с 17:13, пауза началась до перезапуска сервиса.
2026-08-01 19:03:46 +03:00

6.9 KiB

Why

Приём работает третьи сутки, но дальше архива данные не идут: 89 доставок лежат телами .json.gz, а в SQLite только строки delivery. Ни одна из трёх целей проекта — агент-медик, трекер тренировок, фитнес-игра — не может прочитать ничего, потому что читать нечего. Каталог, Read API, MCP и OpenAPI упираются в эту задачу.

Правила разбора не надо изобретать: они выведены измерением на живом потоке и записаны в docs/local-research.md (находки 30, 33, 35, 36, 38, 39, 41). Задача — перенести их в код, а не спроектировать заново.

What Changes

  • Секция metrics тела доставки разбирается в точки: метрика, слой, метка времени, содержимое как пришло.
  • Слой выводится из выравнивания меток, а не из заголовка HAE: плотная метрика (≥10 точек) классифицируется сама, редкая наследует преобладающий слой доставки. Заголовок automation-aggregation непригоден — значение Default соответствует трём разным режимам.
  • Точки складываются в часовые объекты (bucket, ключ метрика + слой + час), содержимое — gzip-BLOB. Запись — read-modify-write со слиянием.
  • Идентичность точки — координаты (метрика + слой + начало + конец; у точки-измерения конец равен началу). Ключ одной формы для всех точек: под одной меткой лежит до трёх записей сна, и 33 столкновения происходят внутри одной доставки, где тай-брейк по времени приёма неприменим. source в ключ не входит: он нестабилен и меняется задним числом. При столкновении выигрывает более полная точка, а не последняя пришедшая.
  • Канонизация с округлением чисел до ~12 значащих цифр; хеш канонической формы — детектор изменений, а не ключ.
  • sleep_analysis разводится на два имени: поэпизодное и суточную сводку — под одним именем HAE шлёт две несовместимые схемы.
  • Метка точки разбирается из локального времени со смещением и хранится в UTC плюс офсет исходной зоны. Эпоха внутри heartbeatSeries не разбирается — серия проходит исходными байтами; RFC 3339 встречается только в stateOfMind и нормируется вместе с ним, отдельной задачей.
  • Разбор не влияет на код ответа приёма: непонятое содержимое по-прежнему даёт 200, исход разбора виден в delivery.parse_status и в логе.

Не входит в изменение (сознательно, чтобы задача мерджилась целиком):

  • тренировки и секции с собственными id (workouts, stateOfMind, прочие record) — отдельная задача, у них другая модель хранения;
  • healthlog reindex — отдельная задача. Следствие: разбираются только доставки, пришедшие после выката; накопленные 89 доедут пересборкой. Сходимость на них проверяется скриптом поверх архива, а не командой сервиса;
  • словарь категориальных значений (переведённые строки → коды HealthKit) — отдельная задача; пока строка хранится дословно и без кода рядом;
  • род агрегации и каталог разрезов — отдельная задача, она следующая.

Capabilities

New Capabilities

  • parsing: превращение тела доставки Health Auto Export в точки — вывод слоя, разбор трёх форматов времени, канонизация содержимого, разделение схем под одним именем метрики. Отдельно от хранения потому, что импорт родного экспорта Apple будет другим разбором поверх того же хранилища.
  • storage: идентичность точки, слияние и хранение часовыми объектами — координатный ключ, правило разрешения столкновений, хеш как детектор изменений, признак запечатанного часа.

Modified Capabilities

Нет: openspec/specs/ пуст, приём кодом существует, но спекой не описан и в этом изменении не трогается.

Impact

  • Новые пакеты internal/hae (разбор), internal/canon (каноническая форма, полнота, хеш — общий дом для разбора и хранения) и расширение internal/store (объекты).
  • Миграция internal/store/migrations/00003_bucket.sql — первая миграция после приёма; вместе с ней заводится docs/database.md (ER-схема), которую требует шаг гейта er-schema.
  • internal/ingest получает шаг разбора после записи в архив; контракт приёма не меняется.
  • Появляется testdata с реальными пакетами HAE. Значения в них вычищаются: структура, порядок ключей, форматы времени, неразрывные пробелы в именах устройств и точность чисел сохраняются, измеренные величины заменяются — данные о здоровье не попадают под контроль версий.